游戏开发日志 (其2𝒾):摄像机神秘抖动事件

发表于: 4/28/2023

Unity

摄像机移动

摄像机抖动

突然发现还没有关于最开始就完成的 “切屏” 部分的内容,也是突然发现这东西还有必要拉出来说一嘴的价值。

一切的起因

虽然在RTS类游戏里大部分人的浏览信息的方式都是鼠标移到屏幕边缘 “拖拽” 过去,不过这更像是表达一种“我要操作那边的单位,给我过去”的想法,而这样也挺麻烦的,有时候我们只是想稍微往那边看看。 在我游戏的过程中发现了一种更为舒适的移动屏幕的方式 “中键拖屏” (什么键都可以啦),按下那个键,屏幕(或者说地图,或者说摄像机)就会跟随鼠标开始移动。

随意的实现 精确的计算

在最初的版本中,通过记录按下 “那个键” 时的鼠标位置和摄像机位置,在鼠标移动时通过系数放大Input.mousePosition的变化量将其施加给摄像机。

Camera.main.transform.position = camPos - Camera.main.orthographicSize / 20 * CameraSpeed * (Input.mousePosition - initialPos);

通过精心调整CameraSpeed和配合orthographicSize的除数,甚至在支持摄像机缩放的情况下拖动可以完美跟手,可喜可贺!

不过这显然存在一个问题,Input.mousePosition到世界坐标是显示相关的,一般来讲用鼠标操作游戏物体时都是要转换的。

//var mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);

能出问题的地方就一定会出问题

因为我有两块不同分辨率的屏幕,把Unity窗口拖过来就发现 “拖动” 不跟手了。

简单的修复

不得不说这就是先人的智慧,你看注释都留好了,只需要小小的修改……

var mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);

Camera.main.transform.position = camPos - (mousePosition - initialPos);

于是就炸了,拖动距离一远摄像机就开始狂抖。

而据Log显示Camera.main.transform.position并不像在Inspector里一样不停来回变化。

把设置摄像机位置的代码移到LateUpdate中有效果吗,并没有。

真·先人的智慧

所幸,在我无数个早夭的项目里发现了这样一段代码:

Vector3 m = new(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
Camera.main.transform.position -= m * mainPlay.MouseSensitivity;

虽然没能避免用系数接近真实的移动,不过把从 “The Key” 按下到抬起过程的大Delta分散到了每一帧,这或许有帮助。

Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Input.GetKey(KeyCode.Mouse2))
{
    Vector3 mouseDelta = mousePos - lastMousePos;
    
    Camera.main.transform.position -= mouseDelta;
}
lastMousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

完美,任何分辨率,任何缩放都完美。

直到最后仍然未解的烂片

其实这个代码并不太符合我的风格,在同一个Update里出现了两个理应是相同的变量进行了两次一样的转换

——我本来是想这么说的,我想了一天一夜,在一个没有午休的中午过后,写到了这里

——于是突然我意识到这两个看似相同的东西实则不同

——摄像机移动了,于是转换的坐标也改变了

而lastMousePos不变则是应该的,我们要的正是 “地图跟随鼠标” 。

所以一切的抖动大抵都出自摄像机移动后,lastMousePos发生漂移,而此前的思路根本上就有问题——跟手的拖动,initialPos与lastPos是相同的,所以每一次Update中计算出的差值并非鼠标全过程的{位移^delta},而是殊途同归的小小增量。