官方文档地址:Rotation and Orientation in Unity
Unity中的旋转,通常用四元数和欧拉角来表示。它们都有优缺点。Unity内部用四元数,在Inspector中显示欧拉角形式,因为该方法已于理解和编辑。
欧拉角由按照顺序在x,y,z轴上进行的旋转表示,当将一个欧拉角应用到一个GameObject时,每个轴上的旋转都会被分别应用。
四元数可以被用来表示旋转或对象的朝向。该数学模型,用4个数来表示(x,y,z,w),然而这些数字既不表示旋转轴,也不表示旋转角度,无法理解,所以我们不能直接修改xyz分量的值(w表示角度,可能会发生错误。除非你非常了解这背后的数学原理。
小秘密:轴和角度藏在四元数中。假设沿着轴(nx,ny,nz)旋转,角度为A度,则四元数为:
[w, x, y, z] = [cos(A/2), sin(A/2)nx, sin(A/2)ny, sin(A/2)nz]
Vector3可以表示位置和方向(当表示方向时,时原点在000点),一个四元数可以表示朝向和旋转,当表示旋转时,是从identity开始的旋转。四元数不能表达超过180度的旋转。
Unity用四元数来存储GameObject的旋转,因为四元数更加健壮。
但是在Inspector面板上,显示的旋转是用欧拉角来表示的,因为欧拉角是可读可编辑的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1KdbpY1X-1580048145871)(https://docs.unity3d.com/uploads/Main/InspectorRotationEulerAngles.png)]
所以,可以看到,如果我们可以在Inspector中设置x:0,Y:365,Z:0来设置对象的旋转,因为四元数无法表达该旋转,因此,启动游戏后,会变成X:0,Y:5,Z:0。效果相同。
当在脚本中处理旋转时,应该用四元数来创建或修改旋转值。当然有些情况下,使用欧拉角也是有效的,但是要牢记,要使用四元数接口来处理欧拉角,从四元数获取,修改,重新应用欧拉角到四元数,可能会产生错误的结果。
Unity提供了接口,用来创建和操作四元数,而且不需要欧拉角的参与。例如
然而,有时我们希望在代码中使用欧拉角,这时,要将角度存储到自己定义的变量中,并且用这些变量更新旋转。因为以获取->修改->应用欧拉角的流程写代码,会出错,因为每次取得的欧拉角,是临时从四元数计算出来的,每次计算可能返回不同的欧拉角,可能会导致万向锁问题。
// 正确
float x;
void Update ()
{
x += Time.deltaTime * 10;
transform.rotation = Quaternion.Euler(x,0,0);
}
// 错误
void Update ()
{
var angles = transform.rotation.eulerAngles;
angles.x += Time.deltaTime * 10;
transform.rotation = Quaternion.Euler(angles);
}
很多3D创作插件,以及Unity内部,都有自己的动画窗口,来用欧拉角编辑动画的旋转。
这些旋转,经常超过四元数表达的范围,比如旋转720度,欧拉角是没问题的,但是对于四元数来说,等于没有旋转。
Unity内部的动画编辑窗口,提供了设置选项,来决定如何进行插值,用四元数或欧拉角。通过指定使用欧拉角,可以告诉Unity我们想要在完整的运动范围内插值。如果用四元数,则会限制在限定的范围内(-180,180),并用四元数插值。
导入的外部动画资源,文件通常会使用欧拉角记录旋转。默认Unity会重新进行采样动画生成新的四元数动画帧,以避免旋转超出四元数有效范围。
例如,想象有2帧动画,相隔6帧。第一帧旋转x=0,第二帧(6帧后)旋转是270。如果直接用四元数在这两帧之间进行插值,则动画会沿着最短路径旋转,即向相反方向旋转90度。但是通过重新采样,将旋转定义为6帧,没帧是45度,这样在进行四元数插值时,就正确了。
有时候,通过重新采样,也不能达到跟原始动画完全重合,这时,可以在动画导入设置中,关闭重新采样选项(Resample Curves),在运行时使用欧拉角进行插值。