游戏中的数学知识
游戏制作中需要用到的数学知识,核心是线性代数、三角函数、微积分和概率论,它们分别支撑着空间变换、运动模拟、物理交互和随机逻辑等关键环节。
- 什么是代数?
- 代数是研究数量关系、结构及其运算规律的数学分支,其核心是通过符号(如字母)抽象表示数与关系,将具体计算升华为普适的数学方法。
符号的含义
∈ 是数学中表示元素属于某个集合的专用符号,读作"属于"。
· 点乘 和 标量乘法
× 叉积 和 向量乘法
什么是归一化(Normalize)
向量归一化是将任意非零向量转换为长度(模)为1的单位向量,同时保持其原始方向不变的数学操作。其核心目的是消除向量长度的影响,仅保留方向信息,
- 游戏领域归一化 = 向量单位化(L2 归一化):
- 将向量转换为 长度 1 的单位向量,分量范围 [-1, 1]
- 归一化后的向量分量一定在 -1 到 1 之间(包含边界值)
📏 线性插值
Lerp
概念:线性插值是一种 通过 两个已知数据点 构造直线 ,估算其之间未知点数值的数学方法,
- 线性插值是用直线填补两点间的空白,它简单到极致,但也仅在线性世界中准确。
公式1:
t 必须 ∈ [0, 1]
y = A + (B - A) × t
公式2:
应用场景
- 动画与角色移动
- 平滑过渡:在角色从位置A移动到位置B时,通过线性插值计算中间帧坐标。
// Unity中实现角色1秒内从startPos移动到endPos
transform.position = Vector3.Lerp(startPos, endPos, Time.time * speed);
-
UI动态效果
- 进度条/血条平滑变化:当玩家血量从100%降至80%,直接赋值会导致突变,而线性插值实现渐变:
-
图像与颜色处理
- 颜色渐变:在UI设计中,从红色 (255,0,0)(255,0,0) 渐变到绿色 (0,255,0)(0,255,0) 时,对RGB通道分别插值:
📐 三角函数
正弦、余弦和正切是描述角度与直角三角形边长比例关系的基本三角函数,其核心本质是将角度的大小转化为可计算的比值,从而将几何问题代数化。以下从直角三角形和单位圆两个层面分步解释:
一、直角三角形中的定义(初中阶段)
1. 正弦(sine)
-
定义:\sin \theta = \dfrac{\text{对边}}{\text{斜边}} = \dfrac{a}{c}。
-
关键特性:
- 角度增大时,正弦值单调递增(如 \sin 30^\circ = 0.5,\sin 60^\circ \approx 0.866)。
- 取值范围:0 < \sin \theta < 1(锐角范围内)。
2. 余弦(cosine)
-
定义:\cos \theta = \dfrac{\text{邻边}}{\text{斜边}} = \dfrac{b}{c}。
-
关键特性:
- 角度增大时,余弦值单调递减(如 \cos 30^\circ \approx 0.866,\cos 60^\circ = 0.5)。
- 取值范围:0 < \cos \theta < 1(锐角范围内)。
3. 正切(tangent)
-
定义:\tan \theta = \dfrac{\text{对边}}{\text{邻边}} = \dfrac{a}{b}。
-
关键特性:
- 角度增大时,正切值单调递增(如 \tan 30^\circ \approx 0.577,\tan 60^\circ \approx 1.732)。
二、单位圆中的扩展定义(高中阶段)
1. 单位圆定义法
- 正弦:\sin \theta = y(终边与单位圆交点的纵坐标)。
- 余弦:\cos \theta = x(终边与单位圆交点的横坐标)。
2. 关键突破
- 角度范围扩展:
- 直角三角形仅限 0^\circ < \theta < 90^\circ,而单位圆定义适用于任意实数角度(如 \theta = 120^\circ、\theta = -30^\circ)。
- 符号规律(象限法则):
- 一全正:第一象限 \sin \theta > 0,\cos \theta > 0,\tan \theta > 0。
- 二正弦:第二象限 \sin \theta > 0,\cos \theta < 0,\tan \theta < 0。
- 三正切:第三象限 \sin \theta < 0,\cos \theta < 0,\tan \theta > 0。
- 四余弦:第四象限 \sin \theta < 0,\cos \theta > 0,\tan \theta < 0。
三、核心关系与实用意义
1. 基本恒等式
- 正切与正余弦关系:\tan \theta = \dfrac{\sin \theta}{\cos \theta}(需 \cos \theta \neq 0)。
2. 实际应用场景
- 导航定位:通过角度和距离计算水平位移 b = c \cdot \cos \theta。
总结
直观记忆技巧
- 直角三角形口诀:
- 正弦:对斜比(对边比斜边)。
- 余弦:邻斜比(邻边比斜边)。
- 正切:对邻比(对边比邻边)。
- 单位圆动态理解:
- 想象单位圆上一点绕原点旋转。
- 正弦、余弦、正切的本质是角度与边长比例的函数关系:直角三角形定义适用于锐角,单位圆定义可推广至任意角。
- 实际应用中,它们将几何问题转化为代数计算,是解决测量、物理、工程等领域周期性问题的核心工具。
- 关键注意:在非锐角范围内,三角函数值可能为负,需结合象限符号规则判断。
虚幻引擎 旋转体 变量 可存储角度值
🔴 正弦(sine) 在游戏中的应用
正弦函数(Sine)是游戏开发中实现“丝滑感”和“自然动态”的灵魂。它的核心数学原理在于其周期性和平滑的波动特性。
在数学上,正弦函数描述了一个在 -1 到 1 之间无限循环、平滑过渡的数值变化。在游戏引擎中,我们通常会将其扩展为一个带有物理意义的通用公式:
y(t)=A⋅sin(2πft+ϕ)+B
其中各个参数的数学与物理意义如下:
- y(t) :随时间变化的输出值(比如物体的坐标、透明度、缩放比例等)。
- A (振幅 Amplitude):控制波动的最大范围(高度)。在游戏中决定了物体“浮动得多高”或“摆动得多远”。
- f (频率 Frequency):控制波动的快慢。频率越高,周期越短,动作越快。
- t (时间 Time):通常是游戏引擎中的实时运行时间(如 Unity 中的
Time.time),让数值随着游戏进程不断变化。 - ϕ (相位 Phase):控制波形的初始偏移量。可以用来让多个同样的波动效果错开节奏。
- B (垂直偏移 Offset):将整个波形在数轴上整体抬高或降低,常用于设定物体的基准位置。
基于这个核心公式,正弦函数在游戏中的应用主要体现在以下几个方面:
-
🌊 周期性悬浮与呼吸动画
这是最直观的应用。如果你想让一个宝箱、道具或者 UI 图标在原地优雅地上下浮动,而不是生硬地瞬移,正弦函数是最佳选择。 -
💡 模拟自然界的律动与特效
除了上下浮动,任何需要模拟“有节奏的自然现象”都离不开正弦波。- 应用场景:水面的波纹起伏、烛光的忽明忽暗(改变亮度 Alpha 值)、角色的呼吸扩胸(改变局部缩放 Scale)、甚至心跳特效。
-
🎥 镜头震动(Camera Shake)
当游戏中发生爆炸或受到重击时,摄像机需要快速且无序地抖动来增强打击感。- 数学原理:利用高频的正弦波在短时间内剧烈改变摄像机的局部坐标(Local Position)。为了不让震动显得太有规律,通常会在 X 轴和 Y 轴上使用不同的频率,甚至引入随机数来干扰振幅。
- 公式体现:xshake=Random⋅sin(高频⋅t)⋅震动强度
🔴余弦函数(Cosine)在游戏中的应用
余弦函数(Cosine)和正弦函数是一对密不可分的“孪生兄弟”。在数学本质上,它们都是周期性波动的三角函数,唯一的区别在于相位差了 90 度(\pi/2)。
以下是余弦函数在游戏中的几个核心应用场景:
-
🎞️ 丝滑的插值与平滑过渡(Cosine Interpolation)
这是余弦函数最经典、最高频的应用之一。在游戏中,我们经常需要让一个数值从 A 慢慢变到 B(比如 UI 渐隐、颜色渐变、物体淡入淡出)。如果使用普通的线性插值(Lerp),变化速度是恒定的,会显得非常生硬和机械。
- 数学原理:利用余弦波在波峰(1)和波谷(-1)附近变化率极慢(趋近于0)的特性,我们可以构造出一个“缓入缓出”的 S 形曲线。它能让动画在开始和结束时非常柔和,中间加速,视觉效果极其自然。
-
💡 真实的光照渲染(朗伯余弦定律)
你在游戏中看到的物体表面明暗变化,其底层物理法则就是朗伯余弦定律(Lambert's Cosine Law)。
- 数学原理:当光线照射到一个粗糙表面时,表面的亮度与光线入射方向和表面法线之间夹角的余弦值成正比。简单来说,光线越垂直照射(夹角为0度,\cos(0)=1),表面就越亮;光线越倾斜,表面接收到的能量就越少。
- 代码逻辑(GLSL Shader 示例):
// normal 是表面法线,lightDir 是指向光源的方向(两者均为单位向量)
// 它们的点积(dot)在数学上就等于夹角的余弦值
float brightness = max(dot(normal, lightDir), 0.0);
// 也可以直观地写成: float brightness = max(cos(theta), 0.0);
这行简单的代码决定了漫反射光照的强度,是实时渲染中最基础的运算之一。
-
⚙️ 完美的圆周运动与旋转(配合 Sin)
正如之前提到的,圆周运动的本质就是三角函数。余弦函数通常负责控制水平方向(X轴)的坐标变化,而正弦函数负责垂直方向(Y/Z轴)。- 实战应用:无论是炮塔绕着基地旋转巡逻,还是摄像机围绕主角做360度环绕拍摄,底层都在实时计算
x = cos(time * speed) * radius。
- 实战应用:无论是炮塔绕着基地旋转巡逻,还是摄像机围绕主角做360度环绕拍摄,底层都在实时计算
-
🌊 程序化特效生成(波纹与噪声)
在模拟水面涟漪、地形起伏或者复杂的纹理扰动时,开发者通常会叠加多个不同频率、振幅和相位的正弦与余弦波。- 数学原理:通过余弦函数对纹理的 UV 坐标进行周期性的偏移,可以制造出逼真的水纹扭曲感。同时,在生成程序化地形(Procedural Generation)时,利用余弦插值可以让随机生成的山峰高度过渡得更加平滑连续,避免出现尖锐的锯齿。
-
💡 总结:正弦与余弦的分工记忆法
你可以把它们看作是一对默契的搭档:- 正弦(Sin):是**“从零开始的节奏大师”**。适合处理从静止状态启动的上下浮动、呼吸灯等效果。
- 余弦(Cos):是**“追求极致的平滑工匠”。只要涉及到“两个状态之间的完美过渡”(如插值)或者“基于角度的能量衰减”**(如光照),余弦函数往往是更优雅、更符合物理规律的选择。
🔴 向量
什么是向量
向量,简单来说就是 “带方向的量。
如果说普通的数字(标量)只告诉你“有多少”,那么向量不仅告诉你“有多少”,还告诉你“往哪去”。
为了让你更直观地理解,我们可以从生活、游戏和数学三个维度来看:
1. 生活中的例子:导航指令
- 标量(普通数字):“往前走 500米”。
- 这里只有距离(大小),没有方向。你只知道要走多远,但不知道往东还是往西。
- 向量:“往 正北方向 走 500米”。
- 这里既有距离(大小),又有明确的方向。这就是一个典型的向量。
2. 游戏中的例子:速度与位移
结合你之前问的“速度存储”,游戏里的速度就是一个完美的向量例子。
- 只有大小(速率):汽车时速 100 km/h。(你不知道车是往前开还是倒车,甚至是在漂移)
- 向量(速度):
(0, 0, 100)。- 这告诉引擎:X轴不动,Y轴不动,Z轴(前方)以 100 的速度前进。
- 如果变成
(0, 0, -100),那就是倒车。 - 如果变成
(100, 0, 0),那就是横着平移。
3. 数学上的样子:箭头与坐标
在数学和计算机图形学中,向量通常有两种表现形式:
几何形式:带箭头的线段
- 长度:代表向量的大小(模)。比如箭头的长短代表速度的快慢或力的大小。
- 指向:代表向量的方向。箭头指哪,就往哪去。
代数形式:一组数字(坐标)
在二维平面中,向量写作 (x, y);在三维空间中,写作 (x, y, z)。
- 例如向量 \vec{v} = (3, 4)。
- 这意味着:从原点出发,向右走 3 步,向上走 4 步,到达终点。这个“从起点到终点的过程”就是向量。
4. 向量能做什么?
在游戏开发和物理模拟中,向量是万能的工具,因为它可以进行运算:
- 加法:你在船上向前走(速度向量A),船也在向前开(速度向量B),你的实际对地速度就是 A+B。
- 减法:计算敌人离你有多远、在哪个方向(目标位置 - 我的位置 = 指向目标的向量)。
- 乘法(数乘):想让速度变快一倍?把向量乘以 2。想倒车?把向量乘以 -1。
- 点积与叉积:这是进阶用法,用来判断“敌人是否在我面前”(点积)或者“计算垂直于地面的法线方向”(叉积)。
一句话概括:向量就是游戏世界里描述“位置、移动、受力、朝向”的最基本语言。
二维向量
\vec v=(x,y)
三维向量
\vec v=(x,y,z)
🟠 向量所在的空间
因为很多教程会告诉你:Vector3(x,y,z)
但不会告诉你:同样是三维向量,它们在数学意义上其实完全不同
在数学里:向量本身没有“身份”它只是:(x,y,z)
真正决定它含义的是:它所在的“空间”和:你如何解释它这点极其重要。因为:
方向向量
法线向量
速度向量
虽然数据格式都一样:(x,y,z) 但数学意义完全不同。
⬇️ 三维向量最核心的分类
| 类型 | 本质 | |
|---|---|---|
| Position | 位置 | (空间中的一个点) |
| Direction | 方向 | (朝哪里在哪里) |
| Velocity | 速度 | (每秒移动多少v⃗=\frac{\Delta x}{\Delta t},速度没有固定位置,它只是移动趋势) |
| Normal | 法线 | (光照依赖表面朝向,垂直向量最能代表平面朝向) |
| Tangent | 切线 | 表面“横向” |
| Euler/Rotation | 旋转表达 | 注意:它不是向量,虽然写成:(x,y,z)它表示:绕三个轴旋转角度 |
方向向量为什么通常 normalize? 因为方向不应该包含距离所以长度通常设为1
Position 与 Direction 最大区别?Position 会受平移影响,Direction 不会受平移影响
-
为什么矩阵里 Position 和 Direction 乘法不同?
-
Position 使用:(x,y,z,1),Direction使用:(x,y,z,0)
- 背后原理(齐次坐标)
- 第四维:
w决定:是否受平移影响 w=1说明:这是空间中的点会被平移。w=0说明:这是纯方向不受平移。
-
-
法线为什么必须垂直?
- 因为它定义:表面方向,垂直向量最能代表平面朝向
-
为什么需要 Tangent?
- 因为法线贴图:不是世界空间,而是:局部表面空间。所以必须建立:TBN 坐标系
-
为什么它们必须正交?
- 因为:坐标系必须稳定,数学上:互相垂直 避免形变。
-
叉积为什么能得到 Right?例如:right=forward×up
- 因为叉积本质:生成垂直于两者的新方向
-
为什么欧拉角会万向节锁?
- 因为:三个旋转轴会重合,导致:丢失一个自由度
-
Quaternion(四元数)
- 它也不是普通向量。
- 虽然:(x,y,z,w)
- 本质:旋转空间中的复数
🔴🔴向量运算
向量运算是游戏开发、物理模拟和计算机图形学的核心数学基础。它不仅能描述空间中的位置与方向,还能通过一系列代数运算解决碰撞检测、光照计算、运动控制等复杂问题。下面从基本运算、几何意义、公式推导和实际应用四个维度,为你系统梳理向量的运算方式与用法。
一、基本运算与公式
1. 向量加法
- 公式:\vec{a} + \vec{b} = (a_x + b_x, a_y + b_y, a_z + b_z)
- 几何意义:将两个向量首尾相接,从第一个向量的起点指向第二个向量的终点,即为和向量。
- 应用: 角色移动与位置更新
应用场景
- 角色在场景中按方向移动(如玩家控制角色前进)。
- 数学原理:
- 位置向量 \vec{p} 表示当前坐标,方向向量 \vec{v} 表示移动方向(需归一化)。
- 每帧更新位置:\vec{p}_{\text{new}} = \vec{p} + \vec{v} \cdot \text{speed} \cdot \Delta t。
- 关键点:方向向量 \vec{v} 必须归一化(单位长度),否则移动速度会受原始向量长度影响。
- 示例:
若角色位置 \vec{p} = (10, 0, 5),移动方向 \vec{v} = (0, 0, 1)(归一化后),速度 5 单位/秒,帧间隔 \Delta t = 0.02 秒,则新位置:
\vec{p}_{\text{new}}=(10,0,5)+(0,0,1)⋅5⋅0.02=(10,0,5.1)
2. 向量减法
- 公式:\vec{a} - \vec{b} = (a_x - b_x, a_y - b_y, a_z - b_z)
- 几何意义:从向量 \vec{b} 的终点指向向量 \vec{a} 的终点的向量。
- 应用:计算两点间的相对位移;AI追踪目标时,用 “目标位置 - 自身位置” 得到朝向目标的向量。
应用场景
- 敌人自动追踪玩家、导弹锁定目标。
- 数学原理:
- 通过减法计算相对位置向量:\vec{d} = \text{玩家位置} - \text{敌人位置}。
- 归一化 \vec{d} 得到移动方向,再通过加法更新敌人位置。
- 关键点:减法结果 \vec{d} 天然包含方向与距离信息,模长 \|\vec{d}\| 可用于判断是否进入攻击范围。
- 示例:
敌人位置 \vec{e} = (2, 3, 0),玩家位置 \vec{p} = (5, 7, 0),则:
\hat{d}=(5−2,7−3,0−0)=(3,4,0),∥d∥=5(距离)- 敌人移动方向:\hat{d} = \vec{d} / \|\vec{d}\| = (0.6, 0.8, 0)。
- 碰撞检测与距离判断
应用场景: 检测两个物体是否相交(如子弹击中敌人)。
数学原理:
计算两物体中心位置向量的差: \vec{d} = \vec{p}_1 - \vec{p}_2
比较 \|\vec{d}\| 与两物体半径之和:若 \|\vec{d}\| < r_1 + r_2,则发生碰撞。
关键点:减法结果的模长直接等于欧几里得距离,避免重复计算平方根(可用 \|\vec{d}\|^2 < (r_1 + r_2)^2 优化性能)。
示例: 两个球体半径均为 11,中心距离 \|\vec{d}\| = 1.8 < 2,判定为相交。
3. 标量乘法(数乘)
- 公式:k \cdot \vec{a} = (k \cdot a_x, k \cdot a_y, k \cdot a_z)
- 几何意义:改变向量的长度,方向不变(若 k > 0)或反向(若 k < 0)。
- 应用:调整移动速度;缩放力的大小;归一化前的长度调整。
4. 向量模长(长度)
-
公式:\|\vec{a}\| = \sqrt{a_x^2 + a_y^2 + a_z^2}
- |\vec v|=\sqrt{x^2+y^2}
- |\vec v|=\sqrt{x^2+y^2+z^2}
-
几何意义:向量在空间中的实际长度。
-
应用:计算两点间距离;判断物体是否在攻击范围内。
-
在ue中获取向量长度

- 为什么长度公式是平方根?
- 因为“长度”本质上是在回答一个问题:两个点之间最短距离是多少?
- 而二维空间里的最短距离,来自欧几里得几何,也就是勾股定理。
- 假设一个向量是
(x,y),你可以把它想象成:从原点(0,0)走到(x,y)。这会形成一个直角三角形,其中:横边长度是x竖边长度是y向量本身是斜边于是根据勾股定理:x^2+y^2=c^2 所以斜边长度就是:c = \sqrt{(x² + y²)}。这就是为什么向量长度公式一定有平方根。因为前面的x² + y²实际上算出来的是:“长度的平方” 而不是长度本身。
5. 单位向量(归一化)
-
公式:\hat{a} = \frac{\vec{a}}{\|\vec{a}\|}
- \hat v=\frac{\vec v}{|\vec v|}
-
几何意义:保留原向量方向,但长度变为1。
-
应用:表示纯方向(如摄像机朝向、武器瞄准方向);避免速度受原始向量长度影响。
- 为什么“除以长度”就能变成单位向量?
- 因为这相当于:把所有分量按同一比例缩小。例如
(3,4)长度是5,那么:\left(\frac35,\frac45\right)- 新长度:\sqrt{(\frac35)^2+(\frac45)^2}=1 由于所有分量同比例缩放,所以方向不会改变,只是长度变成了
1。
二、核心点积与叉积运算
1. 点积
- 公式:\vec{a} \cdot \vec{b} = a_x b_x + a_y b_y + a_z b_z = \|\vec{a}\| \|\vec{b}\| \cos\theta
- 几何意义:衡量两个向量的“相似程度”,结果是一个标量。当 \theta = 0^\circ 时最大,\theta = 90^\circ 时为0,\theta = 180^\circ 时最小。
- 数学性质
- 正负性决定方向关系:这是游戏中最常用的特性。当两个向量都经过归一化(长度为1)时,点积的结果就等于 cos 𝜃,其数值含义如下:
结果 > 0:夹角为锐角(小于90°),两向量大致朝向同一侧。
结果 = 0:夹角为直角(等于90°),两向量互相垂直。
结果 < 0:夹角为钝角(大于90°),两向量大致朝向相反两侧。
结果 = 1:方向完全相同(夹角0°);结果 = -1:方向完全相反(夹角180°)。
- 正负性决定方向关系:这是游戏中最常用的特性。当两个向量都经过归一化(长度为1)时,点积的结果就等于 cos 𝜃,其数值含义如下:
- 应用
- 计算光照强度(光线方向与表面法线的点积决定明暗)。
- 投影计算(将一个向量投影到另一个向量上)。
- 判断物体是否在视野内(点积 > 0 表示在前方)。
在游戏中的应用
🔴 AI 视野检测(是否在扇形范围内?)
⬇️ b炮塔指向玩家的归一化向量 a是炮塔的向前向量。判读敌人是否在30度角的范围内 30度的余弦值大约是0.866(归于话向量)

在游戏中判断敌人是否在玩家的视野内,或者玩家是否看到了某个宝箱,本质上就是判断两个方向的夹角是否小于视野范围的一半。
- 数学原理:利用点积的正负性和余弦值大小。
- 实现逻辑:
- 获取玩家的面朝方向向量
Forward(单位向量)。 - 计算从玩家指向目标的向量
ToTarget,并将其归一化。 - 计算两者的点积。如果点积大于预设的阈值(例如视野角为90度,则阈值为 cos(45∘)≈0.707cos(45∘)≈0.707 ),说明目标在视野内。
- 获取玩家的面朝方向向量
- 优势:相比直接计算角度(需要昂贵的反三角函数),点积只需要简单的乘法和加法,性能极高。
🔴 实时光照计算(兰伯特光照模型)
你看到的物体明暗变化,底层大多依赖点积。比如经典的漫反射(Diffuse)光照。
- 数学原理:光线照射到表面时,入射角越接近垂直(法线方向),表面接收到的光能越多,看起来就越亮。
- 实现逻辑:
- 获取物体表面的法线向量
Normal(垂直于表面的单位向量)。 - 获取指向光源的方向向量
LightDir(单位向量)。 - 计算
dot(Normal, LightDir)。 - 结果越接近 1,光照越强;结果为 0 或负数时,说明光线平行或从背面照射,该区域变暗(通常用
max(0, dot_result)截断负值)。
- 获取物体表面的法线向量
🔴 前后方位判定与背刺机制
如何判断敌人是在你的正前方还是正后方?或者在动作游戏中,如何判定一次攻击是否触发了“背刺”伤害加成?
- 数学原理:利用点积的正负性。
- 实现逻辑:
- 计算
dot(自身面朝方向, 指向敌人的方向)。 - 若结果 > 0,敌人在前方;若结果 < 0,敌人在后方。
- 同理,若计算
dot(敌人的面朝方向, 指向玩家的方向)得到负值,说明玩家在敌人的背后,此时可触发背刺判定。
- 计算
🔴 向量投影(斜坡移动与受力分解)

当角色在斜坡上行走时,重力并不会把他垂直往下拉死,而是会让他沿着斜坡向下滑。这就需要把重力向量“投影”到斜坡的表面上。
- 数学原理:点积的几何意义之一就是投影。向量 a⃗ 在向量 b⃗ 上的投影长度可以通过点积求得。
- 实现逻辑:
- 将重力向量投影到斜坡的切线方向上,就能算出角色沿斜坡下滑的分力。
- 在赛车游戏中,计算车速在赛道前进方向上的分量(有效速度),也需要用到投影。
📐 1. 核心数学原理:如何用点积求投影?
我们的目标是求出重力在法线方向上的投影。根据向量投影公式,
\vec{G} 在 \vec{N} 上的投影向量 \vec{P}_{normal} 为: \vec{P}_{normal} = \left( \frac{\vec{G} \cdot \vec{N}}{|\vec{N}|^2} \right) \vec{N}
由于在游戏引擎(如 Unity、Unreal)中,法线 \vec{N} 已经是**归一化(长度为1)**的单位向量,所以 |\vec{N}|^2 = 1,公式可以简化为: \vec{P}_{normal} = (\vec{G} \cdot \vec{N}) \vec{N}
这里的点积 (\vec{G} \cdot \vec{N}) 算出的就是一个标量(即重力在法线方向的“有效长度”),再乘以方向 \vec{N},就得到了完整的压力向量。
🔴 上坡与玩家移动控制(投影的另一种妙用)
除了分解重力,当你控制角色主动上坡时,点积和投影同样至关重要。
假设玩家在水平地面上输入了一个向前的移动指令(比如按下了 W 键),产生了一个水平的输入方向向量 \vec{I}。如果直接把这个向量加给站在斜坡上的角色,角色可能会因为方向不贴合地面而“腾空”或者“钻入地下”。
为了让角色完美贴合斜坡行走,我们需要把玩家的输入向量 \vec{I} 投影到斜坡所在的平面上。
● 操作方法:使用“投影到平面”的逻辑(在 Unity 中对应 API Vector3.ProjectOnPlane)。它的底层数学原理其实就是从原向量中减去它在法线上的投影:
- \vec{I}_{斜坡} = \vec{I} - (\vec{I} \cdot \vec{N}) \vec{N}
结果:得到的 \vec{I}_{斜坡} 就是一个完全平行于斜坡表面的纯移动方向。无论坡度多陡,角色都会老老实实地贴着地面向上爬,而不会发生穿模或悬空。
2. 叉积
公式: \vec{a} \times \vec{b} = (a_y b_z - a_z b_y, a_z b_x - a_x b_z, a_x b_y - a_y b_x)
几何意义:结果是一个垂直于 \vec{a} 和 \vec{b} 所在平面的新向量,其长度为 \|\vec{a}\| \|\vec{b}\| \sin\theta,方向由右手定则确定。
应用:
- 计算平面的法线(如地面、墙壁的法线用于碰撞和光照)。
- 判断旋转方向(如角色转向时,叉积可判断左转还是右转)。
- 生成垂直于两个方向的第三个方向(如角色面朝方向与上方向叉乘得到右方向)。
🔴应用场景
向量叉积(Cross Product)是游戏开发中处理三维空间问题的核心数学工具。如果说点积是用来衡量“方向相似度”的,那么叉积的核心作用就是用来寻找“垂直方向”和判断“左右转向”。
在游戏引擎中,叉积的应用非常广泛且硬核,以下是它在游戏中的几大核心应用场景:
-
🧭 1. 构建角色的局部坐标系(求“右手边”在哪里)
在3D游戏中,角色或摄像机通常只知道自己的“面朝方向(Forward)”和“头顶方向(Up)”。但为了计算侧向移动、播放侧翻动画或挂载武器,程序必须知道角色的“右手边(Right)”到底是哪个方向。- 应用原理:利用叉积可以求出同时垂直于 Forward 和 Up 的新向量。
- 实际操作:
Right = Cross(Forward, Up)。通过右手法则,这个运算能瞬间帮角色建立起完整的三维本地坐标系。
-
💡 2. 实时光照与渲染(计算法线 Normal)
你在游戏中看到的物体明暗、光影反射,底层都依赖于物体表面的“法线”(即垂直于表面的向量)。- 应用原理:任意一个三角形面片(Mesh的基本单元),只要知道它的三个顶点 A、B、C,就能算出两条边向量 AB 和 AC。这两条边的叉积,就是垂直于该面的法线。
- 实际操作:
Normal = Cross(B - A, C - A)。显卡拿到法线后,才能结合光源方向计算出逼真的漫反射和高光效果。
-
⚔️ 3. 判断目标的左右方位(左右判定)
在赛车游戏或格斗游戏中,系统需要精准判断对手是在你的左边还是右边,或者玩家的攻击是否命中了敌人的左侧弱点。
- 应用原理:叉积的结果向量不仅垂直于原向量,其方向还遵循右手法则。在Unity等左手坐标系中,通过观察叉积结果特定分量(通常是Y轴)的正负,就能直接得出左右关系。
- 实际操作:计算
Cross(自身面朝方向, 指向目标的方向)。如果结果的 Y 分量大于 0,说明目标在你的右侧;小于 0 则在左侧。
-
🏀 4. 物理碰撞反弹(求反弹轨迹)
当球体撞击到斜面墙壁时,如何计算它弹出的真实轨迹?这需要用到入射方向和墙面的法线。- 应用原理:在处理复杂的3D碰撞响应、摩擦力计算以及旋转力矩(比如推门时的旋转轴)时,叉积是必不可少的工具。它能帮助物理引擎确定物体撞击后的旋转轴心以及正确的反弹平面。
-
🗺️ 5. 2D平面中的方向裁判(顺逆时针与区域检测)
在2D游戏或UI交互中,叉积虽然不产生垂直向量,但其计算结果的正负号具有极强的几何意义,被称为“方向裁判”。- 应用原理:在二维平面上,叉积退化为一个标量。它的正负可以直接反映两个向量的相对转向关系。
- 实际操作:
- 判断多边形顶点顺序:依次计算相邻边的叉积,根据正负号判断顶点是顺时针还是逆时针排列(用于图形渲染的背面剔除)。
- 点在旋转矩形内检测:对于任意角度旋转的矩形(OBB),可以通过判断目标点是否在四条边的同一侧(利用叉积正负号一致性)来精准实现点击判定,这在塔防游戏斜向警戒区、RTS技能范围判定中极其常用。
-
💡 总结:叉积的直观记忆法
你可以把叉积想象成游戏世界的 “空间法则制定者” :- 在3D中:它是 “垂直制造机” 。给它两个方向,它立刻给你一个与之都垂直的新方向(用来定法线、定左右手)。
- 在2D中:它是 “方向红绿灯” 。给它两个方向,它通过正负号告诉你这是左转(逆时针)还是右转(顺时针)。
掌握了叉积,你就掌握了在虚拟世界中定义“上下左右”以及处理一切立体空间交互的底层钥匙。
三、进阶运算与应用场景
1. 向量投影
- 公式:\text{proj}_{\vec{b}} \vec{a} = \left( \frac{\vec{a} \cdot \vec{b}}{\|\vec{b}\|^2} \right) \vec{b}
- 几何意义:将向量 \vec{a} “压”到向量 \vec{b} 上的分量。
- 应用:角色在斜坡上移动时,计算沿斜坡方向的位移分量;物理引擎中分解力的分量。
2. 向量反射
- 公式:\vec{r} = \vec{a} - 2(\vec{a} \cdot \hat{n}) \hat{n}(其中 \hat{n} 是表面法线单位向量)
- 几何意义:模拟 光线 或 物体撞击平面 后的 反弹方向。
- 应用:子弹反弹、镜面反射、角色滑墙效果。
3. 向量插值
- 线性插值(Lerp):\vec{c} = \vec{a} + t(\vec{b} - \vec{a}),其中 t \in [0, 1]
- 球面插值(Slerp):用于平滑旋转插值,保持角速度恒定。
- 应用:角色平滑移动、摄像机跟随、动画过渡。
四、总结:向量运算在游戏开发中的核心价值
| 运算类型 | 核心作用 | 典型应用场景 |
|---|---|---|
| 加减法 | 位置更新、相对位移 | 角色移动、AI追踪 |
| 数乘 | 速度/力的大小调整 | 加速、减速、缩放 |
| 模长 | 距离计算 | 攻击范围、碰撞检测 |
| 归一化 | 提取纯方向 | 瞄准、朝向控制 |
| 点积 | 角度/相似度判断 | 视野检测、光照计算 |
| 叉积 | 生成垂直向量/法线 | 平面法线、旋转方向 |
| 投影/反射 | 分解/反弹方向 | 斜坡移动、子弹反弹 |
| 插值 | 平滑过渡 | 动画、摄像机跟随 |
掌握这些运算,你就能在游戏引擎中精确控制物体的运动、交互和视觉效果,是实现复杂游戏逻辑的数学基石。
📦 矩阵
❔️矩阵是什么
在 3 D 游戏引擎(如Unity、Unreal)中,矩阵通常是一个 4 x 4 的数字表格(4行4列,共16个数字)。
你可以把它想象成一个**“空间变换的集装箱”**。它把平移、旋转、缩放这三种最基础的动作,全部打包塞进了这16个数字里。当我们需要移动或旋转一个角色时,不需要写一堆复杂的逻辑,只需要让这个角色的顶点坐标去“乘”这个矩阵,角色就会瞬间完成所有动作。
⚙️矩阵的三大功能
- 在游戏开发中,矩阵最核心的作用就是处理 仿射变换。具体来说,它能把以下三种动作完美融合:
- 平移(Translation):把物体从A点移动到B点。
- 旋转(Rotation):让物体绕着X、Y或Z轴转动。
- 缩放(Scaling):把物体变大、变小,或者压扁、拉长。
- 在底层的 4 x 4 矩阵中,这些动作各自占据了不同的位置:
- 左上角的 3 x 3 区域:负责掌管旋转和缩放。
- 第四列(或第四行,取决于引擎标准)的前三个数:负责掌管平移(即物体在X、Y、Z轴上移动了多少距离)。
- 右下角的 1:是为了配合“齐次坐标”存在的,它保证了平移操作能用矩阵乘法来实现。
🎬 矩阵乘法与变换串联
- 矩阵串联
- 假设你有一个飞碟,你想让它先原地旋转,再飞到天空的某个位置
- 笨办法:先遍历飞碟的所有顶点做一次旋转计算,再遍历一次做平移计算
- 矩阵的办法:把“旋转矩阵”和“平移矩阵”直接相乘,生成一个**“复合矩阵”**。然后只需要遍历一次顶点,乘上这个复合矩阵,飞碟就既旋转又平移了。
- 假设你有一个飞碟,你想让它先原地旋转,再飞到天空的某个位置
📹 MVP矩阵 : 从 3 d 模型转换为 2 d 像素位置
-
MVP 其实是三个矩阵的缩写,它们按顺序相乘得到:
- M_{MVP}=M_{Projection}×M_{View}×M_{Model}
- 注意顺序很重要:在数学计算中,通常是从右向左生效的(模型 × 视图 × 投影 )
-
M_{Model} 模型矩阵
-
作用:负责**“摆放物体”**。
-
功能:它把物体从自己的“局部坐标系”(原点通常在物体中心)搬运到“世界坐标系”的某个位置。
-
包含操作
- 平移:把树从原点移到 (10, 0, 5)。
- 旋转:让树随风转动 30 度。
- 缩放:把小树变成大树(放大 2 倍)。
-
M_{View} 视图矩阵
-
作用:负责**“架设摄像机”**。
-
功能:它把“世界坐标系”转换成“摄像机坐标系”。
-
原理:正如我们之前聊的,为了计算方便,它不是移动摄像机,而是反向移动整个世界。如果你让摄像机后退 10 米,视图矩阵就会让所有物体前进 10 米。
-
目的:确定哪些物体在摄像机面前,哪些在背后。
-
M_{Projection} 投影矩阵
-
作用:负责**“拍照压扁”**。
-
功能:它把 3 D 的视锥体(摄像机看到的金字塔形区域)挤压成一个标准的立方体(通常是 -1 到 1 的范围),并产生近大远小的效果。
-
类型
- 透视投影:有近大远小效果,用于 3 D 游戏(如《原神》、《C S : GO》)。
- 正交投影:没有近大远小,物体大小不变,用于 2 D 游戏或工程图纸(如《纪念碑谷》)。
-
数学计算过程(顶点着色器中的流程)
在图形管线中,每一个顶点(Vertex)都要经历这个“MVP 之旅”。
- 假设有一个顶点的原始位置是 P_{local} (比如模型的一个角),它的变换过程如下:
- 模型变换:P_{world}=M_{model}×P_{local} (把顶点放到世界的大地图里)
- 视图变换:P_{view}=M_{view}×P_{world}(把顶点转换到摄像机面前)
- 投影变换:P_{clip}=M_{project}×P_{view}(把顶点压扁,变成“裁剪空间”坐标)
- 最终公式(也就是我们在 Shader 代码中最常见的一行):P_{clip}=M_{MVP}×P_{local}
🧮 矩阵运算
📍 平移矩阵 (Translation)
- 假设我们要把物体在 X、Y、Z 轴上分别移动 xx 、 yy 、 zz 的距离。
- 平移矩阵的公式就是把这三个数填到第四列,其他地方保持“单位矩阵”(对角线为1,其余为0)的状态:
- 平移计算实例
计算效果:当你用这个矩阵去乘一个点 (x_{0},y_{0},z_{0},1) 时,根据“行乘列”规则,新坐标就变成了
🔴平移计算实例
假设游戏里有一个小怪兽,它身上的一个顶点(比如鼻尖)的原始坐标是 P(1,2,3) 。在矩阵运算中,我们需要把它写成齐次坐标的列向量形式:
目标:把小怪兽向右移动 10 米(X轴+10),向上移动 5 米(Y轴+5),Z轴不动。
平移矩阵 T (填入 x=10,y=5,z=0):
开始计算(矩阵乘向量):规则是“矩阵的每一行”去点乘“向量的每一列”。
- 第1行算新X: (1×1)+(0×2)+(0×3)+(10×1)=1+10=11
- 第2行算新Y: (0×1)+(1×2)+(0×3)+(5×1)=2+5=7
- 第3行算新Z: (0×1)+(0×2)+(1×3)+(0×1)=3+0=3
- 第4行算新W: (0×1)+(0×2)+(0×3)+(1×1)=1
结果:新坐标变成了 (11,7,3) 。
结论:你看,X 真的加了 10,Y 真的加了 5,平移成功!
🔍 缩放矩阵 (Scaling)
假设我们要把物体在 X、Y、Z 轴上分别放大 S_{x}、 S_{y} 、 S_{z}倍。
缩放矩阵的公式就是把这三个缩放系数,直接填在左上角的对角线上:
计算效果:用它乘以一个点 (x_{0},y_{0},z_{0},1) ,新坐标就变成了
(x_{0} \cdot S_{x} , y_{0} \cdot S_{y} , z_{0} \cdot S_{z}) 。
🔍 缩放计算实例
目标:把小怪兽整体放大 2 倍。
缩放矩阵 S (填入 Sx=2,Sy=2,Sz=2 )
开始计算:
- 第1行算新X: (2×1)+0+0+0==2
- 第2行算新Y:0+(2×2)+0+0=4
- 第3行算新Z:0+0+(2×3)+0=6
- 第4行算新W:0+0+0+(1×1)= 1
结果:新坐标变成了 (2,4,6)。
结论:原本的 (1,2,3) 全部乘以了 2,缩放成功!
🔄 旋转矩阵 (Rotation)
旋转稍微复杂一点,需要用到三角函数(\sin 和 \cos)。在游戏中,旋转通常是绕着 X、Y、Z 三个轴分别进行的。
1 . 绕 X 轴旋转 \theta 角( pitch,抬头/低头): X 坐标保持不变,Y 和 Z 发生变化。
2. 绕 Y 轴旋转 \theta **角(yaw,左右转头):**Y 坐标保持不变,X 和 Z 发生变化。
3. 绕 Z 轴旋转\theta **角(roll,歪头):**Z 坐标保持不变,X 和 Y 发生变化。
**🔄**旋转计算实例(绕 Z 轴转 90 度)
为了计算方便,我们假设绕 Z 轴旋转 90 度( θ=90∘θ=90∘ )。
此时数学上: cos(90°)=0 , sin(90°)=1 。
旋转矩阵 R_{z} :
开始计算:
- 第1行算新X: (0×1)+(−1×2)+0+0=−2
- 第2行算新Y: (1×1)+(0×2)+0+0=1
- 第3行算新Z:0+0+(1×3)+0=3
- 第4行算新W:0+0+0+(1×1)=1
结果:新坐标变成了 (−2,1,3) 。
结论:你可以想象一下,原本在 XY 平面上 (1,2)的点,绕着原点逆时针甩了 90 度,确实会跑到 (−2,1) 的位置,Z 轴高度保持不变。旋转成功!
🧩 终极公式:TRS 复合变换矩阵
在实际游戏开发中,我们通常会把**平移(T)、旋转(R)、缩放(S)**打包进一个矩阵里。 如果不考虑复杂的旋转顺序,一个标准的 TRS 变换矩阵长这样(假设旋转后的基向量为右向量 r、上向量 u、前向量 f):
简单总结怎么算:
- 平移:把 X, Y, Z 的位移量填到矩阵的第4列前三个格子。
- 缩放:把 X, Y, Z 的缩放倍数乘到左上角 3x3 区域的对应轴上。
- 旋转:把算好的 sin\sinsin 和 cos\coscos 值填进左上角的 3x3 区域,用来改变物体的朝向。
只要记住这三个矩阵的样子,你就掌握了游戏引擎里 Matrix4x4.TRS 这个函数底层的数学公式了!
复合矩阵实例
在计算机图形学中,复合变换的核心原则是:矩阵乘法不满足交换律。这意味着 T \times R \times S 和 S \times R \times T 的结果完全不同。
业界最通用的标准顺序是 TRS(先缩放、再旋转、最后平移)。下面我将以一个具体的“机械臂关节”实例,带你手算一遍完整的复合矩阵构建过程。
🎯 实例场景:放置一个倾斜的长方体柱子
假设我们有一个长方体模型,它的原点在几何中心。我们需要将它变换到世界空间中的指定位置:
- 缩放 (S):沿 Y 轴拉长 2 倍(X、Z 保持不变)。
- 旋转 (R):绕 Z 轴逆时针旋转 45°。
- 平移 (T):移动到世界坐标 (3,4,0)。
⚠️ 关键提醒 在列向量体系(OpenGL / Vulkan / Unity)中,矩阵是左乘顶点的,即 P_{new} = M \times P_{old}。因此复合矩阵的书写顺序与执行顺序相反: M_{composite} = T \times R \times S (计算时先从右边的 S 开始作用于顶点)
① 第一步:构建基础变换矩阵
- 缩放矩阵 S
S = \begin{bmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 2 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
- 旋转矩阵 R(绕 Z 轴旋转 \theta = 45° )
R_z = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 0.707 & -0.707 & 0 & 0 \\ 0.707 & 0.707 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
- 平移矩阵 T
T = \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 & 3 \\ 0 & 1 & 0 & 4 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
② 第二步:按 TRS 顺序相乘得到复合矩阵
先算 R \times S(旋转 × 缩放)
R \times S = \begin{bmatrix} 0.707 & -0.707 & 0 & 0 \\ 0.707 & 0.707 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 2 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 0.707 & -1.414 & 0 & 0 \\ 0.707 & 1.414 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
再算 T \times (R \times S)(平移 × 上一步结果)
M_{TRS} = \begin{bmatrix} 1 & 0 & 0 & 3 \\ 0 & 1 & 0 & 4 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \times \begin{bmatrix} 0.707 & -1.414 & 0 & 0 \\ 0.707 & 1.414 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 0.707 & -1.414 & 0 & 3 \\ 0.707 & 1.414 & 0 & 4 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
💡 核心规律:在列向量体系中,平移矩阵左乘任何矩阵,只会改变该矩阵的第 4 列(平移分量),前三列完全不变。这就是为什么我们总是把平移放在最后乘——它不会干扰旋转和缩放的基向量。
③ 第三步:验证复合矩阵的正确性
取模型局部空间的原点 (0,0,0,1)^T 代入验证:
M_{TRS} \times \begin{bmatrix} 0 \\ 0 \\ 0 \\ 1 \end{bmatrix} = \begin{bmatrix} 3 \\ 4 \\ 0 \\ 1 \end{bmatrix}
原点被正确平移到了 (3,4,0),且没有受到缩放和旋转的影响(因为原点本身没有方向)。
再取局部空间的 Y 轴顶点(0,1,0,1)^T 验证:
M_{TRS} \times \begin{bmatrix} 0 \\ 1 \\ 0 \\ 1 \end{bmatrix} = \begin{bmatrix} -1.414 + 3 \\ 1.414 + 4 \\ 0 \\ 1 \end{bmatrix} = \begin{bmatrix} 1.586 \\ 5.414 \\ 0 \\ 1 \end{bmatrix}
- Y 轴先被缩放为 2 倍长度;
- 再绕 Z 轴旋转 45°,指向了左上方(X 为负,Y 为正);
- 最后整体平移到 - (3,4,0)的基础上偏移。 结果完全符合预期。
| 错误顺序 | 后果 |
|---|---|
| 先平移再旋转 (R \times T \times S) | 物体会绕着世界原点公转,而不是绕自身中心自转,位置会严重偏离预期 |
| 先平移再缩放 (S \times T \times R) | 平移量也会被缩放!比如平移 3 个单位,缩放 2 倍后实际移动了 6 个单位 |
| 先旋转再缩放 (S \times R \times T) | 缩放会沿着世界坐标轴进行,而不是沿着物体旋转后的局部轴,导致物体被“压扁”而非“拉长” |
取模运算
-
取模运算:x % y(或x mod y),表示x除以y的余数。
-
取余和取模的区别
- 通常取模运算(mod)也叫取余(.rem)运算,它们返回结果都是余数
- .rem 和 mod 唯一的区别在于:
当 x 和 y 的正负号一样的时候,两个函数结果是等同的;当 x 和 y 的符号不同时,rem 函数结果的符号和 x 的一样,而 mod 和 y 一样。 - 取余和取模的区别 | 菜鸟教程 (runoob.com)
-
进制运算与转换
-
科学技术法
评论区