Mathf.Atan2 return 不正确的结果

Mathf.Atan2 return incorrect result

有时当我移动鼠标(围绕角色旋转相机)时,我的角色会旋转。 但是我没有通过鼠标进行任何字符旋转。 这是我的旋转代码,仅取决于键盘输入(我从标准统一示例中获取此代码):

private void ConvertMoveInput()
{
    Vector3 localMove = transform.InverseTransformDirection(_moveInput);
    _turnAmount = Mathf.Atan2(localMove.x, localMove.z);
    _forwardAmount = localMove.z;
}

我发现有时当我移动鼠标 Mathf.Atan2 return 数字 PI 时,即使两个参数仍然等于零。这是怎么发生的?

我的演示项目here

tan函数是周期性的,周期为π。因此在 0π 上调用 tan 会产生相同的值(即零)。

这就是您不能简单地使用 atan 来恢复原始角度的原因之一 - 您正在丢失大量原始信息,并且您将获得的角度将永远是在 -π/2+π/2 之间。

Unity 中的

Atan2 旨在解决此问题 - 它采用 xy 坐标,而不是它们的除法(这就是你如何使用 atan).然后它可以计算出角度的正确 象限 - 适当地偏移它。同样重要的是,它处理 y 坐标为零的情况——这通常会导致被零除错误。例如,[1; 0] 可能 return 0°,[0; 1] 270°(注意 - 我不确定确切的数字;这取决于 "reference" 的位置,并且这个我没在Unity3D中用过,重点是原理,不是具体数值)。

但是,这只有在至少有一个坐标是零的情况下才能工作-坐标[0; 0] 相对于 [0; 0] 没有任何有意义的角度。所有角度都同样有效(或者两者都不有效,取决于您的观点)。 Unity 只是从所有可能的选项中选择 π - 无论出于何种原因。它真的和其他任何东西一样物有所值。唯一真正的替代方法是抛出一个异常并让你的游戏崩溃——这通常不会比像这样的小故障更受欢迎:)

记住,atan2(x, z) returns是z轴和向量之间的夹角(x, z):

但是对于 atan2(0, 0),您试图找到一个点与 z 轴或 x 轴之间的角度,这是没有意义的:

即使 atan2(0, 0) 没有意义,如果 xy 是有效数字,在 atan2(0, 0) 的特殊情况下,return 值是 pi。 The implementation of atan2() in the C++ standard library does this, 我想很多语言,包括 Unity 的 C# 实现,都效仿了。

这意味着您需要检查 xz 均为 0 的特殊情况。例如:

if ((localMove.x != 0) && (localMove.z != 0)) {
  _turnAmount = Mathf.Atan2(localMove.x, localMove.z);
}
else {
  _turnAmount = 0;
}