Unity中的旋转:彻底搞懂欧拉角与四元数

在 Unity 中,我们经常写出类似这样的代码:

transform.eulerAngles = new Vector3(0, 90, 0);

或者:

transform.rotation = Quaternion.Euler(0, 90, 0);

看似只是控制旋转的两种写法,其实背后隐藏着一个巨大的数学鸿沟:
欧拉角 (Euler Angles)四元数 (Quaternion)
这篇文章我们就把它讲清楚——不仅告诉你区别,还让你理解 Unity 为什么要用“四元数”这种反人类的方式来表示旋转。


一、欧拉角:人类友好的旋转

欧拉角是最直观的旋转方式。
它用三个角度(X、Y、Z)来描述物体分别绕三个坐标轴的旋转量。

举个例子:

含义 对应轴 例子
Pitch(俯仰) X轴 头上下点
Yaw(偏航) Y轴 左右转头
Roll(滚转) Z轴 头歪一边

在 Unity 中:

transform.eulerAngles = new Vector3(30, 90, 0);

表示物体先绕 X 轴转 30°,再绕 Y 轴转 90°,最后绕 Z 轴转 0°。

看起来很直观,但欧拉角有一个致命缺点——万向锁(Gimbal Lock)


二、万向锁:旋转自由度的消失

万向锁是指在某些角度组合下,旋转的三个自由轴发生重叠,导致某一维度的旋转“失效”。
举个常见例子:

想象你拿着一个陀螺仪,它可以分别绕三个方向旋转。
如果你把它先绕 X 轴旋转 90°,那么 Y 轴和 Z 轴会重叠,结果你失去了一个旋转自由度——陀螺仪不能再独立转动那一维了。

在 Unity 中,这种情况就会导致:

  • 插值(比如 Lerp)不连续;
  • 旋转动画出现“跳动”;
  • 一些角度计算莫名其妙出错。

这就是为什么我们不能完全依赖欧拉角进行旋转计算。


三、四元数:机器友好的旋转

四元数是一种数学结构,形式上是四个数 (x, y, z, w)
它可以完美描述三维旋转,而不会出现万向锁问题。

Unity 中的 Quaternion 类型,就是对四元数的封装。

比如:

transform.rotation = Quaternion.Euler(0, 90, 0);

其实内部是:

先把欧拉角 (0, 90, 0) 转换成四元数,再存到 rotation 属性里。


四元数的优势:

  1. 不会万向锁
    因为它不依赖顺序旋转三个轴,而是用一种“整体旋转”的方式表示。

  2. 插值平滑
    Unity 的 Quaternion.LerpSlerp 可以实现平滑旋转,非常自然。

  3. 运算高效
    四元数在旋转合成(比如多段旋转叠加)时,计算量更小且更准确。


缺点也很明显:

四元数几乎不可读
Quaternion(0.707, 0, 0.707, 0) 这样的数字完全没有直觉含义。
这就是为什么 Unity 同时保留了两个属性:

属性 类型 含义
transform.eulerAngles Vector3 可读可写的欧拉角(单位:度)
transform.rotation Quaternion 实际存储的四元数旋转

四、Unity源码中的 eulerAngles 实现

在 UnityEngine 的源码里,Transform.eulerAngles 是这样定义的:

public Vector3 eulerAngles
{
    get => this.rotation.eulerAngles;
    set => this.rotation = Quaternion.Euler(value);
}

这意味着:

  • 当你读取 transform.eulerAngles 时,Unity 会从内部的四元数 rotation 转换回欧拉角;
  • 当你赋值给 eulerAngles 时,它会把你的人类角度再转换成四元数存回去。

换句话说:

Unity 一直用四元数在工作,只是给你提供了欧拉角的“友好界面”。


五、实际开发建议

  1. 在 Inspector 或编辑逻辑中用欧拉角

    • 因为它直观。
    • 例如手动调整角度、配置初始朝向。
  2. 在代码和运行时逻辑中用四元数

    • 比如控制旋转、插值、合成等。
    • Quaternion.LerpQuaternion.LookRotationQuaternion.Euler
  3. 避免频繁在欧拉角和四元数间来回转换

    • 转换不是完全可逆的,会有数值误差。

六、一个常见的坑:旋转叠加

很多人写出类似这样的代码:

transform.eulerAngles += new Vector3(0, 90, 0);

结果旋转越来越奇怪。

原因是每次取 eulerAngles 时,Unity 会重新把四元数转换成欧拉角(有可能产生不同的角度表示),于是误差就不断积累。

正确写法是:

transform.rotation *= Quaternion.Euler(0, 90, 0);

这才是真正意义上的“在原旋转基础上增加90°”。


七、总结一句话

概念 人类友好 数学友好 是否会万向锁 用途
欧拉角 (Euler Angles) 编辑器显示、初始设置
四元数 (Quaternion) 实际计算、插值、物理旋转

Unity 选择四元数并不是为了让开发者痛苦,而是为了让游戏世界稳定地旋转


理解四元数,是理解三维空间的入门仪式。
一旦你跨过这一步,旋转再也不会让你晕头转向。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐