欧拉角和四元数
Unity 中欧拉角与四元数使用指南
1. 四元数基础
四元数可以表示旋转,形式为:
q = [cos(β/2), sin(β/2) * x, sin(β/2) * y, sin(β/2) * z]
- β:旋转角度(弧度)
- (x, y, z):旋转轴单位向量
单位四元数
Quaternion q = Quaternion.identity; // 物体初始朝向(无旋转)
2. 欧拉角与四元数在Unity中的表现以及相互转换
欧拉角与四元数在Unity中的表现
✔ 欧拉角(EulerAngles)在 Unity 中:范围是 0° 到 360°(循环)
✔ 四元数表示的角度范围是 -180° 到 180°(归一化后的角度)
当我们想要使用欧拉角旋转角度时,最好不要使用小于0的角度来控制,当使用小于0的angle时,会自动修正为360+angle
this.transform.eulerAngles = new Vector3(-10, 0, 0);
float f = Mathf.Clamp(this.transform.eulerAngles.x, -20, 20);
this.transform.eulerAngles = new Vector3(f, 0, 0);
//Unity编辑器中观察物体的旋转结果为(20, 0, 0);
欧拉角转四元数
Vector3 eulerAngles = new Vector3(30, 45, 60);
Quaternion q = Quaternion.Euler(eulerAngles);
四元数转欧拉角
Vector3 eulerAngles = q.eulerAngles;
注意:使用欧拉角旋转容易出现万向节死锁(Gimbal Lock),例如 X 旋转 90° 时,Y 和 Z 轴旋转会耦合。
3. 常用旋转操作
3.1 欧拉角旋转(会造成万向节死锁)
//常用的Rotate三种重载
//重载一
//参数 eulers 可以分解为 angleX,angleY,angleZ 表示绕物体自身X轴/Y轴/Z轴 每帧旋转angleX度/angleY度/angleZ度
this.transform.Rotate(Vector3 eulers);
//绕物体局部坐标系的Y轴 每帧旋转1度
this.transform.Rotate(Vector3.up);
//等价于 (不常用)
this.transform.Rotate(0, 1, 0)
//重载二
//以物体的局部坐标系 将参数eulers转换为物体的旋转轴 围绕eulers向量旋转轴每帧旋转angle度
this.transform.Rotate(Vector3 eulers, float angle)
//绕物体局部坐标系的y轴每帧旋转10度
this.transform.Rotate(Vector3.up, 10)
//重载三
//Space是一个枚举类enum,有两个枚举成员:World和Self 分别表示以世界坐标系算出eulers旋转轴方向 以物体局部坐标系算出eulers旋转轴方向
//不填写Space参数时 默认使用的是Self 即物体局部坐标系
this.transform.Rotate(Vector3 eulers, float angle, Space relativeTo)
//绕世界坐标系的y轴每帧旋转10度
this.transform.Rotate(Vector3.up, 10, Space.World);
3.2 四元数旋转(推荐,避免万向节死锁)
- 围绕局部坐标轴旋转
//直接赋值旋转
this.transform.rotation = Quaternion.AngleAxis(float angle, Vector3 Axis);
//让物体的AngleZ直接等于 -20° (旋转不是的顺时针旋转 而是符合左手 左手大拇指朝向旋转轴朝向 四指旋转方向就是物体旋转方向)
this.transform.rotation = Quaternion.AngleAxis(-20, Vector3.forward);
//累加旋转
this.transform.rotation *= Quaternion.AngleAxis(float angle, Vector3 Axis);
//在物体本身的旋转基础上 绕物体的z轴旋转-20°
transform.rotation *= Quaternion.AngleAxis(-20, Vector3.forward); // 每帧旋转 1 度
- 使物体面向目标
方法一:
//重载一 forward表示物体到目标的向量
this.transform.rotation = Quaternion.LookRotation(Vector3 forward);
this.transform.rotation = Quaternion.LookRotation(target.transform.position - this.transform.position);
//重载二 指定upwards为物体的y轴朝向 防止forward轴朝向相同时,up轴和right轴朝向不符合需求
Quaternion.LookRotation(Vector3 forward, Vector3 upwards);
方法二:
//方式一 直接使用 =
this.transform.rotation = Quaternion.FromToRotation(this.transform.position,target.transform.position);
//方式二 使用 *=
this.transform.rotation *= Quaternion.FromToRotation(this.transform.position,target.transform.position);
注意:
在Update里不要使用法向量和物体向量相减,猜测:法向量和物体的向量相减存在误差
cube.transform.rotation = Quaternion.LookRotation(info.normal - cube.transform.up)
上述代码的目的是让cube的y轴重合 法线,但是在Update中,每一帧差值运算结果不会相等。当法线为-45度时,可能左右翻转的原因是:
- 每帧绝对赋值 → 四元数 ±A 取值切换,欧拉角表现为 180° 翻转。
✅ 解决方法:
//方法一:使用叉乘获取cube的x轴朝向,LookRotation直接操作x轴旋转没有误差,
//注意:LookRotation中使用第二个参数info.normal,不然斜面沿物体的z轴倾斜时,物体的y轴和法线不会重合
Vector3 forward = Vector3.Cross(cube.transform.right, info.normal);
cube.transform.rotation = Quaternion.LookRotation(forward, info.normal);
//方法二:使用*=以及FromToRotation,暂时不知道为什么可以解决,直接使用 `=` 会出现抖动 需要使用 `*=`
cube.transform.rotation *= Quaternion.FromToRotation(cube.transform.up, info.normal)
- 平滑旋转到目标物体角度
Quaternion.Slerp(Quaternion a, Quaternion b, float t)
//想要物体慢慢旋转到和目标物体的角度一样时
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, target.transform.rotation, speed*Time.deltaTime);
//想要物体慢慢旋转到看向物体的角度时
this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
Quaternion.LookRotation(target.transform.position-this.transform.rotation),
speed*Time.deltaTime);
3.3 四元数与向量旋转
- 四元数乘向量
Vector3 forward = Vector3.forward; // (0,0,1)
Vector3 newForward = Quaternion.AngleAxis(45, Vector3.up) * forward; // 旋转 45 度后的方向
- 注意顺序问题:
- 标准写法:
Quaternion * Vector3✅ Vector3 * Quaternion在 Unity 中虽然重载支持,但内部仍使用Quaternion * Vector3计算- 数学上顺序不交换,两个顺序结果不等价
- 标准写法:
4. 总结与建议
- 尽量使用四元数进行旋转,避免欧拉角带来的万向节死锁。
Quaternion.AngleAxis可实现任意轴旋转,支持局部旋转累积。Quaternion.LookRotation是物体对准目标方向的标准方法。Quaternion.Slerp用于平滑旋转,常用于摄像机跟随或物体面向目标。

浙公网安备 33010602011771号