前言:
在Unity中,相机的正前方是Z正半轴,相机的正右方是X正半轴,相机的正上方是Y正半轴。这个很好理解。
现在,我想要相机看向左前上方45°,你会觉得要怎么做呢?
如果是我的话,我的第一感觉确实就是先绕相机的Y轴逆时针旋转45度,然后再绕相机的X轴逆时针旋转45度。然后确实是做到了,没错的,大家的第一印象没毛病。
假设这个时候摄像头视野的正中心有一个苹果,但是苹果的正左侧有一个香蕉,现在我想让相机往左稍微摆动一下,让相机聚焦苹果正左侧的香蕉,于是我们打算绕相机的Y轴再次逆时针旋转10度,试图去对准香蕉。
奇怪的是,当旋转完毕之后,相机视野正中心并没有靠近香蕉,甚至是远离了香蕉!!!
越是想纠正,越是远离香蕉!!!
原因:
经过多次旋转之后,已经改变了相对于相机的局部坐标轴(相机的前、右、上方向),所以原来的方法会失效。
这种现象被称为“万向节锁定”(Gimbal Lock)。万向节锁定是一个在使用三个欧拉角表示三维空间旋转时可能遇到的问题,尤其是当旋转顺序导致两个旋转轴对齐时。此时,会失去一个旋转自由度,导致无法独立控制某个轴的旋转。
在Unity等三维图形环境中,当你绕着一个轴旋转一个物体时(比如绕着X轴或Y轴),这个物体的其他轴(比如Y轴和Z轴)也会随之旋转。如果进行多次旋转,这些轴可能会变得对齐或近似对齐,从而导致万向节锁定。在万向节锁定的状态下,本应独立的两个旋转轴因为对齐而实际上合并成了一个轴,使得原本独立的两个旋转方向无法被单独控制。
解决的方法:
1. 用四元数(Quaternion)来表示旋转
为了解决或避免万向节锁定问题,可以使用四元数(Quaternion)来表示旋转。四元数可以提供不受万向节锁定影响的平滑连续旋转,这也是为什么在Unity等现代三维图形软件中四元数被广泛用于处理旋转。
2.还是喜欢用xyz轴表示旋转
如果还是想用3个轴来表示旋转的话,可以将这两个旋转分开到两个不同的对象上,可以确保当你旋转其中一个轴时,不会意外地影响到另外一个轴。这样,摄像头的行为就更加可控和预测。在Unity中,为了避免万向节死锁,并使摄像头的控制更加直观和灵活,通常会将摄像头的水平旋转(通常是Y轴)和垂直旋转(通常是X轴)分开处理。
我的处理方法是,相机这个对象来控制抬头和低头。
相机的父对象Observer来控制左转和右转。
public float mouseSensitivity = 100f;
[SerializeField] Transform observerTf;
[SerializeField] Transform cameraTf;
private float xRotation = 0f;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;
observerTf.Rotate(0, mouseX * Time.deltaTime, 0);
cameraTf.Rotate(-mouseY * Time.deltaTime, 0, 0);
}
一个对象控制一个轴的旋转,这样就能避免旋转的时候意料之外的旋转了Z轴,导致视线不可控的问题。这样一来,相机视线的控制就变得得心应手了。