不,这不是引擎错误或特定旋转表示的人工产物(也可能发生,但是这种效果适用于表示旋转(包括四元数)的每个系统)。
您已经发现有关旋转在三维空间中如何工作的真实事实,它与我们对其他转换(如翻译)的直觉不同:
当我们在一个以上的轴上组合旋转时,得到的结果不只是应用于每个轴的总/净值(我们可能期望平移)。我们应用旋转的顺序会更改结果,因为每次旋转都会移动要应用下一个旋转的轴(如果绕对象的局部轴旋转),或者对象与轴之间的关系(如果绕着世界的轴旋转)。轴)。
轴关系随时间的变化会混淆我们对每个轴“应该”做什么的直觉。特别是偏航旋转和俯仰旋转的某些组合所产生的结果与侧倾旋转相同!
您可以验证每个步骤是否围绕我们要求的轴正确旋转-符号中没有引擎故障或伪影干扰或猜测我们的输入-球形(或超球形/四元数)旋转性质仅意味着我们的变换“自动换行周围”。对于较小的旋转,它们可能在局部正交,但随着它们堆积,我们发现它们不是全局正交的。
对于上述90度转弯来说,这是最生动和清晰的,但如问题所示,漂移轴也会在许多小的旋转中蠕动。
那么,我们该怎么办?
如果您已经有了俯仰-偏航旋转系统,消除不想要的侧倾的最快方法之一就是更改其中一个旋转,以对全局或父变换轴而不是对象的局部轴进行操作。这样一来,您就不会在两者之间产生交叉污染-一个轴仍然受到绝对控制。
这与上面示例中的俯仰-偏航-俯仰变距相同,但是现在我们绕全局Y轴而不是对象的
因此,我们可以使用“本地变音,全球偏航”的口号来修复第一人称摄影机:
void Update() {
float speed = lookSpeed * Time.deltaTime;
transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f, Space.World);
transform.Rotate(-Input.GetAxis("Vertical") * speed, 0f, 0f, Space.Self);
}
如果要使用乘法复合旋转,则可以翻转其中一个乘法的左/右顺序以获得相同的效果:
// Yaw happens "over" the current rotation, in global coordinates.
Quaternion yaw = Quaternion.Euler(0f, Input.GetAxis("Horizontal") * speed, 0f);
transform.rotation = yaw * transform.rotation; // yaw on the left.
// Pitch happens "under" the current rotation, in local coordinates.
Quaternion pitch = Quaternion.Euler(-Input.GetAxis("Vertical") * speed, 0f, 0f);
transform.rotation = transform.rotation * pitch; // pitch on the right.
(具体顺序将取决于您环境中的乘法约定,但是左=全局=右=局部=更多)
这等效于将所需的净总偏航和总螺距存储为浮点变量,然后始终一次应用净结果,仅从这些角度构造一个新的新方向四元数或矩阵(前提是您要保持totalPitch
钳制):
// Construct a new orientation quaternion or matrix from Euler/Tait-Bryan angles.
var newRotation = Quaternion.Euler(totalPitch, totalYaw, 0f);
// Apply it to our object.
transform.rotation = newRotation;
或同等...
// Form a view vector using total pitch & yaw as spherical coordinates.
Vector3 forward = new Vector3(
Mathf.cos(totalPitch) * Mathf.sin(totalYaw),
Mathf.sin(totalPitch),
Mathf.cos(totalPitch) * Mathf.cos(totalYaw));
// Construct an orientation or view matrix pointing in that direction.
var newRotation = Quaternion.LookRotation(forward, new Vector3(0, 1, 0));
// Apply it to our object.
transform.rotation = newRotation;
使用这种全局/局部拆分,旋转没有机会相互影响并相互影响,因为它们已应用于独立的轴集。
如果它是我们要旋转的世界中的物体,则相同的想法可能会有所帮助。对于像地球这样的示例,我们经常想将其反转并在本地应用偏航(因此,它总是绕其两极旋转)并在全球范围内倾斜(因此,它向着/远离我们的视线,而不是朝着/远离澳大利亚的方向倾斜) ,无论指向何处...)
局限性
这种全球/本地混合策略并不总是正确的解决方案。例如,在具有3D飞行/游泳功能的游戏中,您可能希望能够笔直向上/笔直向下并仍具有完全控制权。但是通过这种设置,您会碰到万向节锁定 -偏航轴(全局向上)变得平行于横滚轴(局部向前),并且您无法向左或向右看而不会扭曲。
在这种情况下,您可以做的是使用纯局部旋转,就像我们在上面的问题中开始的那样(这样,无论您在哪里看,您的控件都感觉相同),这最初会使一些滚动发生-但随后我们对此进行纠正。
例如,我们可以使用局部旋转来更新“前向”矢量,然后将该前向矢量与参考“上”矢量一起使用来构造最终方向。(例如,使用Unity的Quaternion.LookRotation方法,或从这些向量手动构建正交矩阵)。通过控制向上向量,我们可以控制滚动或扭曲。
对于飞行/游泳示例,您将希望随着时间的推移逐步应用这些更正。如果太突然,视图可能会分散注意力。取而代之的是,您可以使用播放器的当前向上矢量,并将其朝垂直方向逐帧提示,直到其视线变平为止。与在播放器控件闲置时扭曲相机相比,在转弯中应用此方法有时会更令人讨厌。