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
旨在解决此问题 - 它采用 x
和 y
坐标,而不是它们的除法(这就是你如何使用 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)
没有意义,如果 x
和 y
是有效数字,在 atan2(0, 0)
的特殊情况下,return 值是 pi。 The implementation of atan2()
in the C++ standard library does this, 我想很多语言,包括 Unity 的 C# 实现,都效仿了。
这意味着您需要检查 x
和 z
均为 0 的特殊情况。例如:
if ((localMove.x != 0) && (localMove.z != 0)) {
_turnAmount = Mathf.Atan2(localMove.x, localMove.z);
}
else {
_turnAmount = 0;
}
有时当我移动鼠标(围绕角色旋转相机)时,我的角色会旋转。 但是我没有通过鼠标进行任何字符旋转。 这是我的旋转代码,仅取决于键盘输入(我从标准统一示例中获取此代码):
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
之间。
Atan2
旨在解决此问题 - 它采用 x
和 y
坐标,而不是它们的除法(这就是你如何使用 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)
没有意义,如果 x
和 y
是有效数字,在 atan2(0, 0)
的特殊情况下,return 值是 pi。 The implementation of atan2()
in the C++ standard library does this, 我想很多语言,包括 Unity 的 C# 实现,都效仿了。
这意味着您需要检查 x
和 z
均为 0 的特殊情况。例如:
if ((localMove.x != 0) && (localMove.z != 0)) {
_turnAmount = Mathf.Atan2(localMove.x, localMove.z);
}
else {
_turnAmount = 0;
}