news 2026/10/17 10:53:18

四元数:游戏旋转的幕后高手

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
四元数:游戏旋转的幕后高手

一句话:四元数用 4 个数记录"绕哪根轴、转多少度",把一次旋转当成一个整体来存。它不会死锁、插值平滑、计算便宜,所以引擎内部几乎所有旋转都用它。Unity 的transform.rotation本身就是四元数。

延续上一篇:先讲它是什么、为什么好用,再讲游戏里的具体用法。


一、四元数是什么?

1. 直觉:任何旋转都等于"绕某根轴转一次"

不管物体被转了多少次、怎么转,最终的姿态都可以用一根轴 + 一个角度一步到位。这是数学上的定理(欧拉旋转定理)。

欧拉角:先绕 Y 转 → 再绕 X 转 → 再绕 Z 转(3 步) 四元数:绕这根斜着的轴,一次转 60°(1 步)

2. 4 个数怎么来

假设绕单位轴(ax, ay, az)转 θ 角:

x = ax · sin(θ/2) y = ay · sin(θ/2) z = az · sin(θ/2) w = cos(θ/2)
  • x, y, z:记录轴的方向
  • w:记录转了多少
  • 4 个数平方和恰好等于 1(这种叫单位四元数,只有它表示旋转)
四元数 (x, y, z, w)含义
(0, 0, 0, 1)没有旋转(Quaternion.identity)
(0, 0.707, 0, 0.707)绕 Y 轴转 90°
(0, 1, 0, 0)绕 Y 轴转 180°

为什么是半角 θ/2?因为用四元数旋转一个向量时,计算公式里四元数会被"用两次"(q · v · q⁻¹),两个半角合起来正好是 θ。记住结论就行,日常开发完全不需要手算这 4 个数。

3. 为什么它没有万向节死锁?

欧拉角的问题出在"分 3 步、按顺序转",中间那步转到 90° 时,前后两根轴重合了。

四元数不分步骤,直接记录最终的"轴 + 角",没有"中间那根轴",也就不存在两根轴重合的问题。

4. 和欧拉角、矩阵对比

欧拉角旋转矩阵四元数
存储3 个数9 个数4 个数
人能看懂✅ 很直观❌❌
万向节死锁❌ 有✅ 没有✅ 没有
平滑插值❌ 会绕远路、抖动❌ 插值后会变形✅ 天然平滑
组合旋转麻烦矩阵相乘(较贵)四元数相乘(便宜)

分工:人填欧拉角 → 程序用四元数计算 → 渲染时转成矩阵交给 GPU。


二、最常用的 4 个操作

记住这 4 个,游戏里 90% 的旋转需求都能解决。

1. 旋转一个向量:q * v

Quaternionq=Quaternion.AngleAxis(90,Vector3.up);// 绕 Y 转 90°Vector3v=q*Vector3.forward;// (0,0,1) → (1,0,0)

顺序必须是四元数在左、向量在右,写反了编译不通过。

2. 组合两次旋转:q2 * q1

Quaterniontotal=q2*q1;// 先转 q1,再转 q2(从右往左读)

和矩阵一样,顺序不能交换。在 Unity 里有一个非常实用的规律:

Quaterniondelta=Quaternion.Euler(0,10,0);transform.rotation=transform.rotation*delta;// 绕【自身】的 Y 轴转transform.rotation=delta*transform.rotation;// 绕【世界】的 Y 轴转
delta 放右边 → 自身坐标系(像飞机按自己的机身转) delta 放左边 → 世界坐标系(像地球的竖直方向)

3. 反向旋转:Quaternion.Inverse(q)

把转过去的再转回来。常用于求两个朝向之间的差:

// 从 a 朝向转到 b 朝向,需要多转多少?Quaterniondiff=Quaternion.Inverse(a)*b;

4. 插值:Slerp/Lerp

在两个朝向之间平滑过渡,这是四元数最大的价值,下一节详细讲。


三、游戏中的实际应用

应用 1:让物体朝向目标 ——LookRotation

敌人看向玩家、炮塔瞄准、角色面朝移动方向:

Vector3dir=player.position-transform.position;transform.rotation=Quaternion.LookRotation(dir);// 让 Z 轴(前方)指向 dir

只转水平方向(角色不该因为玩家在高处就仰起身子):

dir.y=0;if(dir.sqrMagnitude>0.0001f)// 方向为零时不要调用transform.rotation=Quaternion.LookRotation(dir);

应用 2:平滑转向 ——Slerp

直接设置朝向会"瞬间转过去",很生硬。用插值让它慢慢转:

Quaterniontarget=Quaternion.LookRotation(dir);transform.rotation=Quaternion.Slerp(transform.rotation,target,5f*Time.deltaTime);

为什么用四元数插值就不会出问题?

欧拉角插值:350° → 10°,可能倒着转 340°;还会在 90° 附近乱甩 四元数插值:永远沿着【最短的那条弧】转过去,速度均匀、不抖动
方法特点适用
Quaternion.Slerp角速度完全均匀镜头、需要精确的动画
Quaternion.Lerp计算更快,中间速度略有不均大量物体、差异肉眼难辨

小提醒:Slerp(当前, 目标, k * deltaTime)是"越接近越慢"的缓动效果,在不同帧率下手感略有差异。追求严格一致时可以用1 - Mathf.Exp(-k * Time.deltaTime)作为插值系数。


应用 3:限速转向 ——RotateTowards

炮塔、坦克炮管通常有固定的最大转速,不是缓动:

publicfloatturnSpeed=90f;// 每秒最多转 90°voidUpdate(){Quaterniontarget=Quaternion.LookRotation(enemy.position-turret.position);turret.rotation=Quaternion.RotateTowards(turret.rotation,target,turnSpeed*Time.deltaTime);// 判断是否瞄准完成(两个朝向之间相差的角度)if(Quaternion.Angle(turret.rotation,target)<2f)Fire();}

Quaternion.Angle直接给出两个朝向相差多少度,判断"瞄准了没有""视野内没有"都很方便。


应用 4:第三人称环绕相机

publicTransformtarget;publicfloatdistance=5f;floatyaw,pitch;voidLateUpdate(){yaw+=Input.GetAxis("Mouse X")*3f;pitch-=Input.GetAxis("Mouse Y")*3f;pitch=Mathf.Clamp(pitch,-30f,70f);Quaternionrot=Quaternion.Euler(pitch,yaw,0);// 人能理解的角度 → 四元数transform.rotation=rot;transform.position=target.position-rot*Vector3.forward*distance;// ↑ 用四元数把"前方"转到相机朝向,再往后退}

这里同时用到了"欧拉角输入"和"四元数旋转向量",是两者分工的典型例子。


应用 5:贴合地面 ——FromToRotation

车辆开上斜坡、角色站在球形星球上,需要让"头顶方向"对齐地面法线:

if(Physics.Raycast(transform.position,Vector3.down,outRaycastHithit,2f)){// 求出"把当前上方转到地面法线"所需的旋转Quaternionalign=Quaternion.FromToRotation(transform.up,hit.normal);Quaterniontarget=align*transform.rotation;// 放左边:世界空间叠加transform.rotation=Quaternion.Slerp(transform.rotation,target,10f*Time.deltaTime);}

FromToRotation(a, b):把方向 a 转到方向 b 的最短旋转,非常实用。


应用 6:骨骼动画与动画混合

角色每根骨骼的旋转都以四元数存储。走路切换到跑步时,两套动画要按比例混合:

最终旋转 = Slerp(走路姿势, 跑步姿势, 混合比例)

如果用欧拉角混合,手臂可能在中间某帧突然甩一圈;用四元数则过渡自然。这也是动画系统(Animator、Timeline)底层都用四元数的原因。


应用 7:物理与陀螺仪

// 刚体旋转:应该用 MoveRotation,而不是直接改 transformrb.MoveRotation(rb.rotation*Quaternion.Euler(0,90f*Time.fixedDeltaTime,0));// 手机陀螺仪直接给出四元数,用于 AR、体感瞄准Quaterniongyro=Input.gyro.attitude;

应用 8:网络同步压缩

多人游戏要每秒同步大量物体的朝向,四元数可以压得很小:

4 个 float = 16 字节 ↓ 利用"平方和 = 1",丢掉最大的那个分量,接收方再算回来 3 个分量 × 10 位 + 2 位标记丢的是哪个 = 32 位 = 4 字节

这叫Smallest Three压缩,很多网络框架都在用。欧拉角很难这样压,因为它存在多种写法和死锁问题。


四、常见的坑

坑说明正确做法
直接改rotation.xx、y、z、w 不是角度,改了会导致四元数失效、物体变形用Euler、AngleAxis等函数构造
乘法顺序写反a * b和b * a结果不同记住:右边先转;增量放右边 = 自身轴,放左边 = 世界轴
LookRotation(Vector3.zero)方向为零,会报警告且结果无意义调用前检查长度
方向几乎竖直时LookRotation抖动前方与默认上方Vector3.up平行,无法确定"上"手动传入合适的up参数
q和-q是同一个旋转自己写混合时若不处理,可能绕远路转若Quaternion.Dot(a, b) < 0,先把 b 取反(Unity 的 Slerp 已自动处理)
多次相乘后误差累积长时间累乘可能不再是单位四元数必要时q.Normalize()

五、常用 API 速查

API作用典型用途
Quaternion.Euler(x, y, z)欧拉角 → 四元数把配置/输入角度转成旋转
Quaternion.AngleAxis(角度, 轴)绕任意轴旋转风车、绕斜轴转动
Quaternion.LookRotation(方向)朝向某方向看向目标、面朝移动方向
Quaternion.FromToRotation(a, b)把 a 转到 b贴合地面、对齐法线
Quaternion.Slerp / Lerp平滑插值转向、动画混合
Quaternion.RotateTowards限速转向炮塔、追踪导弹
Quaternion.Angle(a, b)两朝向的夹角瞄准判断、视野判断
Quaternion.Inverse(q)反向旋转求相对旋转
q * v旋转一个向量计算偏移位置、射击方向

一句话总结

四元数 = 用 4 个数记录"绕哪根轴转多少度",把旋转当成一个整体。因为不分步骤,所以没有万向节死锁;因为能沿最短弧插值,所以转向和动画过渡都很平滑。

实际开发中基本不用关心那 4 个数的值,只要会用:LookRotation看向目标、Slerp平滑转向、RotateTowards限速转、FromToRotation对齐方向、q * v旋转向量、a * b组合旋转,就能覆盖绝大多数需求。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 10:52:24

Ubuntu手动安装内核:从deb包到源码编译的完整实践指南

1. 项目概述&#xff1a;为什么“手动安装内核”不是折腾&#xff0c;而是系统掌控力的分水岭 在Ubuntu生态里&#xff0c;“升级内核”这件事&#xff0c;绝大多数人默认点开“软件更新”就完事——系统自动推送、自动下载、自动安装、自动更新GRUB菜单&#xff0c;整个过程安…

作者头像 李华
网站建设 2026/10/17 10:51:53

Spring Boot多数据源配置:MyBatis双库集成与避坑指南

简介&#xff1a;这份PDF资料面向使用SpringBoot与MyBatis的Java后端开发者&#xff0c;聚焦多数据源配置这一常见痛点&#xff0c;尤其适合需要处理主从模式或分库业务、又不想引入JPA或AOP动态切换等复杂方案的中级开发者。资源为单个PDF文件&#xff0c;压缩包约54KB&#x…

作者头像 李华
网站建设 2026/10/17 10:51:01

FileZilla Server 0.9.39汉化绿色版:免安装免注册表的便携FTP服务器

简介&#xff1a;本资源为FileZilla Server 0.9.39汉化绿色版&#xff0c;面向Windows系统管理员、Web开发者及FTP服务初学者&#xff0c;提供开箱即用的轻量级FTP服务器部署方案&#xff0c;无需安装、不写注册表&#xff0c;适用于局域网文件共享、本地测试环境搭建及教学演示…

作者头像 李华
网站建设 2026/10/17 10:49:10

AI编程沙箱只读模式两次被击穿:执行环境分层隔离方案

先交代一下背景吧。这个标题说的是我自己团队最近遇到的一件事——我们的 AI 编程沙箱&#xff0c;在两个月内被"击穿"了两次&#xff0c;而且第二次是在已经开启了"最严只读模式"的情况下发生的。所谓"最严只读"&#xff0c;就是文件系统层面做…

作者头像 李华
网站建设 2026/10/17 10:47:37

OpenStack多节点部署实战:IaaS云平台搭建与避坑指南

简介&#xff1a;这是一份以OpenStack为核心的IaaS云管理平台毕业设计论文&#xff0c;内容覆盖云计算背景、IaaS层级架构、OpenStack核心组件&#xff08;Nova、Neutron、Cinder、Swift&#xff09;的协同原理&#xff0c;以及平台搭建与部署的完整过程。适合正在撰写云计算方…

作者头像 李华
网站建设 2026/10/17 10:46:35

第2章:Linux 基础,echo 命令

专栏导航 上一篇&#xff1a;第2章&#xff1a;Linux 基础&#xff0c;clear 命令 回到目录 下一篇&#xff1a;第2章&#xff1a;Linux 基础&#xff0c;mkdir 命令的简单入门 本节前言 对于本节所讲解的知识&#xff0c;有可能&#xff0c;你会需要时不时地参考本专栏的其…

作者头像 李华