在 Unreal Engine 的 3D 开发中,旋转(Rotation) 是最基础也最容易让人困惑的概念之一。Unreal 提供了两种主要的旋转表示方式:FRotator(欧拉角) 和 FQuat(四元数)。它们各有优劣,适用于不同场景。本文将系统梳理它们的定义、相互转换、使用场景及常用方法,帮助你写出更健壮、高效的旋转逻辑。
一、基本定义
1. FRotator ------ 欧拉角(Euler Angles)
- 结构 :由三个 
float值组成:Pitch(俯仰,绕 Y 轴)、Yaw(偏航,绕 Z 轴)、Roll(翻滚,绕 X 轴)。 - 单位 :度(Degrees),不是弧度!
 - 可读性 :高。例如 
(0, 90, 0)表示向右转 90 度,直观易懂。 - 范围 :
- 默认范围:
[-360, 360] - 可通过 
GetNormalized()归一化到[-180, 180] 
 - 默认范围:
 
            
            
              cpp
              
              
            
          
          FRotator Rot(30.0f, 45.0f, 0.0f); // Pitch=30°, Yaw=45°, Roll=0°
        ⚠️ 注意 :Unreal 的坐标系是 左手坐标系 ,Z 轴朝上,因此 Yaw 是绕 Z 轴旋转(与 Unity 等右手系不同)。
2. FQuat ------ 四元数(Quaternion)
- 结构 :由四个 
float值组成:X, Y, Z, W,满足X² + Y² + Z² + W² = 1(单位四元数)。 - 本质:一种数学上更优雅的旋转表示,避免了欧拉角的"万向节死锁(Gimbal Lock)"。
 - 可读性 :低。
FQuat(0, 0, 0.707, 0.707)对人类来说毫无意义。 - 优势 :
- 插值平滑(Slerp)
 - 组合旋转高效(四元数乘法)
 - 无奇异性(不会死锁)
 
 
            
            
              cpp
              
              
            
          
          FQuat Quat = FQuat::MakeFromEuler(FVector(0, 90, 0)); // 从欧拉角构造
        二、相互转换
1. FRotator → FQuat
        
            
            
              cpp
              
              
            
          
          FRotator Rot(0, 90, 0);
FQuat Quat = Rot.Quaternion(); // 推荐方式
// 或
FQuat Quat2 = FQuat(Rot);      // 隐式转换(内部调用 Quaternion())
        2. FQuat → FRotator
        
            
            
              cpp
              
              
            
          
          FQuat Quat = ...;
FRotator Rot = Quat.Rotator(); // 推荐方式
        ✅ 重要提示 :
四元数到欧拉角的转换不是一一对应的 !多个四元数可能对应同一个欧拉角(因为旋转有周期性)。因此,不要期望
Quat.Rotator().Quaternion() == Quat恒成立。
三、使用场景对比
| 场景 | 推荐类型 | 原因 | 
|---|---|---|
| 编辑器输入 / 蓝图暴露 | FRotator | 
用户友好,直观可调 | 
| 角色控制 / 摄像机旋转 | FRotator | 
逻辑清晰(如"角色面向 Yaw 方向") | 
| 动画 / 物理 / 插值 | FQuat | 
避免死锁,插值平滑 | 
| 组合多个旋转 | FQuat | 
四元数乘法比欧拉角叠加更准确 | 
| 网络同步旋转 | FQuat | 
数据更紧凑,无角度跳变问题 | 
| 数学计算 / 变换矩阵 | FQuat | 
与 FTransform 内部一致 | 
四、常用方法与技巧
1. FRotator 常用方法
        
            
            
              cpp
              
              
            
          
          // 归一化到 [-180, 180]
FRotator Normalized = Rot.GetNormalized();
// 获取方向向量(常用于角色朝向)
FVector Forward = Rot.Vector(); // 等价于 FRotationMatrix(Rot).GetUnitAxis(EAxis::X)
// 旋转一个向量
FVector NewDir = Rot.RotateVector(OriginalDir);
// 插值(注意:可能经过长路径!)
FRotator Interp = FMath::Lerp(RotA, RotB, Alpha);
FRotator InterpShort = FMath::RInterpTo(RotA, RotB, DeltaTime, InterpSpeed); // 自动选短路径
        ⚠️
FMath::Lerp对FRotator不会自动选择最短路径 !可能绕远路。建议使用FMath::RInterpTo或先转四元数插值。
2. FQuat 常用方法
        
            
            
              cpp
              
              
            
          
          // 从轴角构造
FQuat Quat = FQuat::MakeFromAxisAngle(FVector::UpVector, FMath::DegreesToRadians(90));
// 从两个向量构造(将 From 向量旋转到 To 向量)
FQuat Quat = FQuat::FindBetweenVectors(FromDir, ToDir);
// 球面线性插值(Slerp)------ 平滑旋转
FQuat Interp = FMath::QInterpTo(QuatA, QuatB, DeltaTime, InterpSpeed);
// 组合旋转:先 A 再 B
FQuat Combined = QuatB * QuatA; // 注意顺序!Unreal 是右乘
// 旋转一个向量
FVector NewDir = Quat.RotateVector(OriginalDir);
// 获取旋转轴和角度
FVector Axis;
float Angle;
Quat.ToAxisAndAngle(Axis, Angle); // Angle 是弧度!
        🔁 旋转顺序注意 :
在 Unreal 中,
QuatB * QuatA表示 先应用 A,再应用 B(与矩阵乘法一致)。
五、常见陷阱与最佳实践
❌ 陷阱 1:直接对 FRotator 做 Lerp
        
            
            
              cpp
              
              
            
          
          // 错误!可能从 179° 跳到 -179°,导致旋转 358° 而不是 2°
FRotator BadInterp = FMath::Lerp(RotA, RotB, Alpha);
        ✅ 正确做法:
            
            
              cpp
              
              
            
          
          FQuat GoodInterp = FMath::QInterpTo(RotA.Quaternion(), RotB.Quaternion(), DeltaTime, Speed);
FRotator Result = GoodInterp.Rotator();
        ❌ 陷阱 2:混淆旋转顺序
            
            
              cpp
              
              
            
          
          // 想让物体先绕自身 Y 轴转,再绕世界 Z 轴转?
// 错误写法:
FQuat LocalYaw = FQuat(FRotator(DeltaYaw,0 , 0)); // Pitch,Yaw,Roll -> Y,Z,X
FQuat WorldRoll = FQuat(FRotator(0, DeltaRoll, 0));
Comp->SetRelativeRotation(WorldRoll * LocalYaw); // 顺序可能不对!
        ✅ 明确顺序 :通常局部旋转用 SetRelativeRotation,世界旋转用 AddWorldRotation。
✅ 最佳实践
- 
内部计算用
FQuat,对外接口用FRotator------ 例如:Actor 的
GetActorRotation()返回FRotator,但内部FTransform存储的是FQuat。 - 
插值一律转四元数
------ 避免角度跳变问题。
 - 
网络同步优先传
FQuat------ 更稳定,且
FQuat可压缩(如CompressedRotation)。 - 
不要手动修改
FQuat的 X/Y/Z/W------ 除非你非常清楚自己在做什么,否则容易破坏单位长度。
 
六、引擎内部如何存储?
USceneComponent的RelativeRotation和ComponentToWorld中的旋转,底层都是FQuat。FRotator仅作为用户友好接口 存在,每次设置SetRelativeRotation(FRotator)时,引擎会立即转为FQuat存储。
总结
| 特性 | FRotator | 
FQuat | 
|---|---|---|
| 可读性 | ⭐⭐⭐⭐⭐ | ⭐ | 
| 插值质量 | ⭐⭐ | ⭐⭐⭐⭐⭐ | 
| 组合旋转 | ⭐⭐ | ⭐⭐⭐⭐⭐ | 
| 万向节死锁 | 有风险 | 无 | 
| 适合场景 | 用户输入、简单逻辑 | 动画、物理、数学计算 | 
记住一句话 :
"用FRotator思考,用FQuat计算。"
掌握这两者的区别与转换,你就能在 Unreal 的旋转世界中游刃有余,写出既直观又健壮的代码。