Unity 中的旋转和朝向

文章目录

  • Rotation and Orientation in Unity
  • 欧拉角和四元数
    • 欧拉角
    • 四元数
    • 编程中
      • 直接创建和操作四元数
        • 创建
        • 操作
        • 欧拉角使用注意
    • 动画中
      • Unity 的 Animation Window
      • 导入的动画数据

Rotation and Orientation in Unity

官方文档地址:Rotation and Orientation in Unity

Unity中的旋转,通常用四元数和欧拉角来表示。它们都有优缺点。Unity内部用四元数,在Inspector中显示欧拉角形式,因为该方法已于理解和编辑。

欧拉角和四元数

欧拉角

欧拉角由按照顺序在x,y,z轴上进行的旋转表示,当将一个欧拉角应用到一个GameObject时,每个轴上的旋转都会被分别应用。

  • 好处:欧拉角对于人来说时可读的,可以跟读欧拉角在脑海建立旋转概念。
  • 好处:欧拉角可以表达超过180度的旋转,对应的,四元数会“寻找”旋转的最短路径,但是有时可能逻辑上是不正确的,比如对于一个不能以360度旋转的炮塔,从最左旋转到最有,对端路径会打破旋转限制。
  • 限制:欧拉角受限于万向锁 Gimbal Lock,当在3个轴上旋转时,可能第一次或第二次旋转导致第三次旋转轴指向跟前面旋转的轴同样的方向,叫做“degree of freedom”(旋转自由度)丢失,因为第三个旋转值无法应用到对应的轴上,也可以理解为丢失了一个旋转轴。

四元数

四元数可以被用来表示旋转或对象的朝向。该数学模型,用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度的旋转。

  • 好处:没有万向锁问题
  • 限制:不能表达超过180度的旋转,四元数用-180到180来表示360度的旋转。
  • 限制:不可读,无法直接理解值的含义。

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提供了接口,用来创建和操作四元数,而且不需要欧拉角的参与。例如

创建

  • Quaternion.LookRotation
  • Quaternion.AngleAxis
  • Quaternion.FromToRotation

操作

  • Quaternion.Slerp
  • Quaternion.Inverse
  • Quaternion.RotateTowards
  • Transform.Rotate & Transform.RotateAround

欧拉角使用注意

然而,有时我们希望在代码中使用欧拉角,这时,要将角度存储到自己定义的变量中,并且用这些变量更新旋转。因为以获取->修改->应用欧拉角的流程写代码,会出错,因为每次取得的欧拉角,是临时从四元数计算出来的,每次计算可能返回不同的欧拉角,可能会导致万向锁问题。

// 正确
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 的 Animation Window

Unity内部的动画编辑窗口,提供了设置选项,来决定如何进行插值,用四元数或欧拉角。通过指定使用欧拉角,可以告诉Unity我们想要在完整的运动范围内插值。如果用四元数,则会限制在限定的范围内(-180,180),并用四元数插值。

导入的动画数据

导入的外部动画资源,文件通常会使用欧拉角记录旋转。默认Unity会重新进行采样动画生成新的四元数动画帧,以避免旋转超出四元数有效范围。

例如,想象有2帧动画,相隔6帧。第一帧旋转x=0,第二帧(6帧后)旋转是270。如果直接用四元数在这两帧之间进行插值,则动画会沿着最短路径旋转,即向相反方向旋转90度。但是通过重新采样,将旋转定义为6帧,没帧是45度,这样在进行四元数插值时,就正确了。

有时候,通过重新采样,也不能达到跟原始动画完全重合,这时,可以在动画导入设置中,关闭重新采样选项(Resample Curves),在运行时使用欧拉角进行插值。

你可能感兴趣的:(Unity 中的旋转和朝向)