引言
在 Unity 的 Inspector 中,你看到的是 \((x, y, z)\) 的欧拉角。但在代码底层,旋转通常是存储为四元数 (Quaternion) \((x, y, z, w)\)。 为什么?为什么不直接用三个角度?
1. 欧拉角 (Euler Angles) 的问题
欧拉角非常直观:绕 X 轴转多少度,绕 Y 轴转多少度,绕 Z 轴转多少度。 但它有一个致命缺陷:万向节死锁 (Gimbal Lock)。
当中间的旋转轴(通常是 Y 或 X,取决于旋转顺序)旋转 90 度时,第三个旋转轴会与第一个旋转轴重合。此时,你失去了一个自由度。无论你怎么转第三个轴,效果都和转第一个轴一样。
此外,欧拉角很难进行平滑插值。直接对角度进行 Lerp 会导致奇怪的旋转路径(比如从 350度 转到 10度,会绕一大圈经过 180度,而不是直接跨过 0度)。
2. 什么是四元数?
四元数由 4 个数组成:\((x, y, z, w)\)。 它本质上是轴-角 (Axis-Angle) 表示法的数学扩展。
如果你想绕轴 \(\vec{n} = (n_x, n_y, n_z)\) 旋转 \(\theta\) 度,对应的四元数为: \[ \begin{align*} x &= n_x \cdot \sin(\theta/2) \\ y &= n_y \cdot \sin(\theta/2) \\ z &= n_z \cdot \sin(\theta/2) \\ w &= \cos(\theta/2) \end{align*} \]
3. 四元数的运算
乘法 = 组合旋转
就像矩阵一样,两个四元数相乘表示旋转的叠加。 \[ Q_{final} = Q_{second} \times Q_{first} \] 注意:顺序依然很重要!
逆 (Inverse)
\(Q^{-1}\) 表示相反的旋转。 如果你想“撤销”一个旋转,就乘以它的逆。
常用函数
Quaternion.Identity: 无旋转 \((0, 0, 0, 1)\)。Quaternion.Euler(x, y, z): 将欧拉角转换为四元数。Quaternion.LookRotation(forward, up): 创建一个旋转,使物体看向forward方向。
4. 球面线性插值 (Slerp)
这是四元数最强大的功能之一。 Lerp (线性插值) 是沿着直线走的。对于旋转来说,这意味着它会切过球体内部,导致旋转速度不均匀,甚至变形(如果用于矩阵)。 Slerp (Spherical Linear Interpolation) 是沿着球体的表面(大圆弧)走的。
它保证了旋转是最短路径且匀速的。
// 平滑旋转向目标
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * speed);总结
- 欧拉角: 直观,但有万向节死锁,插值困难。用于 UI 显示。
- 矩阵: 适合组合变换,但存储空间大 (16个数),容易积累误差。
- 四元数: 紧凑 (4个数),无死锁,插值完美 (Slerp)。用于内部存储和计算旋转。
在写代码时,尽量使用 Quaternion 的 API(如
LookRotation, Slerp,
AngleAxis),避免手动操作 \((x,y,z,w)\) 分量。