引言
在打开 Unity 的 Mathf 或 Unreal 的
FMath
文档时,你可能会看到几十个函数。其中有一些函数是如此常用,以至于它们构成了游戏逻辑的基石。
本章不讨论复杂的向量或矩阵,只关注最基础的标量
(Scalar) 运算——即对单个浮点数的操作。
可以把这一篇看成 Unity Mathf / Unreal
FMath
里常用标量函数的“中文说明书”和使用场景合集。
引擎语境:本文主要以 Unity / C# 视角讲解,概念同样适用于 Unreal / Godot / 自研引擎。
1. 线性插值 (Lerp)
Lerp (Linear Interpolation) 可能是游戏开发中最著名的函数。
公式
\[ Lerp(a, b, t) = a + (b - a) \times t \] 或者写作: \[ Lerp(a, b, t) = (1 - t) \times a + t \times b \]
其中: - \(a\): 起始值 (当 \(t=0\)) - \(b\): 终点值 (当 \(t=1\)) - \(t\): 进度/百分比 (通常在 0 到 1 之间)
游戏中的应用
- 平滑移动: 每帧将物体的位置向目标位置移动一点点。
// 简单的平滑跟随
currentPos = Mathf.Lerp(currentPos, targetPos, Time.deltaTime * speed);- 颜色渐变: 随着血量降低,血条颜色从绿色变为红色。
healthColor = Color.Lerp(Color.red, Color.green, healthPercent);- 动画混合: 在两个动画状态之间过渡。
2. 反向插值 (InverseLerp)
如果你知道起始值 \(a\)、终点值 \(b\) 和当前值 \(v\),你想知道 \(v\) 处于 \(a\) 和 \(b\) 之间的什么位置(即求 \(t\)),这就需要 InverseLerp。
公式
\[ t = \frac{v - a}{b - a} \]
游戏中的应用
- 计算进度条: 玩家经验值是
500,当前等级是从 0 到 1000 经验。
progress = InverseLerp(0, 1000, 500)-> 结果是 0.5。 - 根据距离计算音量: 声音在
10米处最大,50米处消失。
volume = 1.0 - InverseLerp(10, 50, currentDistance)
3. 范围映射 (Remap)
这是 InverseLerp 和 Lerp
的组合拳。将一个数值从一个范围映射到另一个范围。
例如:将输入信号 (-1 到 1) 映射到 屏幕坐标 (0 到 1920)。
公式
\[ t = InverseLerp(inMin, inMax, value) \] \[ result = Lerp(outMin, outMax, t) \]
代码实现 (Unity Extension)
public static float Remap(float value, float from1, float to1, float from2, float to2) {
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}游戏中的应用
- UI 布局: 将 HP (0-100) 映射到 UI 宽度 (0-300 像素)。
- 难度调整: 将 玩家等级 (1-60) 映射到 怪物攻击力 (10-5000)。
4. 限制 (Clamp)
将数值限制在指定的范围内。
变体
Clamp(value, min, max): 限制在 [min, max] 之间。Clamp01(value): 限制在 [0, 1] 之间。这是最常用的,特别是在处理百分比或颜色时。
游戏中的应用
- 防止数值越界: 玩家血量不能超过上限,也不能低于 0。
- 摄像机角度限制: 俯仰角限制在 -80度 到 80度之间,防止脖子折断。
5. 平滑步进 (SmoothStep)
Lerp
是线性的,变化率是恒定的。如果你想要“起步慢,中间快,结束慢”的效果,就需要
SmoothStep。它使用一个 S 形曲线 (Sigmoid-like)
进行插值。
公式 (Hermite 插值)
\[ t = Clamp01(t) \] \[ result = t \times t \times (3 - 2 \times t) \]
游戏中的应用
- 更自然的动画: 比如 UI 窗口弹出的动画,或者相机的移动,使用 SmoothStep 会比 Lerp 看起来更有“重量感”。
- 地形生成: 在生成高度图时,平滑噪声值。
6. 接近 (MoveTowards)
MoveTowards 和 Lerp
很像,但有一个关键区别:Lerp
的第三个参数是比例,而
MoveTowards 的第三个参数是最大增量
(MaxDelta)。
公式
\[ result = value + sign(target - value) \times min(abs(target - value), maxDelta) \]
游戏中的应用
匀速移动: 如果你用
Lerp(a, b, Time.deltaTime),物体会随着接近目标而减速(芝诺悖论)。如果你想让物体以恒定速度到达目标,必须用MoveTowards。// 每秒移动 5 米,直到到达 target currentPos = Vector3.MoveTowards(currentPos, target, 5.0f * Time.deltaTime);
7. 循环与摆动 (Repeat & PingPong)
这两个函数通常用于处理时间或循环动画。
重复 (Repeat)
Repeat(t, length) 类似于取模运算
(Modulo),但对浮点数和负数处理得更好。它让数值永远保持在
[0, length] 之间。
公式
\[ result = t - floor(t / length) \times length \]
游戏中的应用
- 无限滚动的背景: 纹理坐标 UV 的循环。
- 循环动画: 让时间 \(t\) 永远在 0 到 1 之间循环。
乒乓 (PingPong)
PingPong(t, length) 让数值在 0
到 length
之间来回摆动。就像乒乓球一样,撞到边界就反弹。
公式
\[ result = length - abs(Repeat(t, length \times 2) - length) \]
游戏中的应用
巡逻的敌人: 在点 A 和点 B 之间来回走动。
呼吸灯效果: 透明度在 0 到 1 之间反复渐变。
// 产生一个 0 -> 1 -> 0 -> 1 ... 的值 float alpha = Mathf.PingPong(Time.time, 1.0f);
总结
| 函数 | 作用 | 典型应用 |
|---|---|---|
| Lerp | 线性插值 (按比例) | 平滑跟随、颜色渐变 |
| InverseLerp | 求比例 | 计算进度条、归一化数据 |
| Remap | 范围转换 | 数值映射、UI适配 |
| Clamp | 限制范围 | 血量限制、角度限制 |
| SmoothStep | S形插值 | 自然动画、地形平滑 |
| MoveTowards | 匀速接近 | 匀速移动、巡逻 |
| Repeat | 循环 | 滚动背景、时间循环 |
| PingPong | 来回摆动 | 呼吸灯、往返巡逻 |
掌握这些基础函数,能让你在写游戏逻辑时少写很多
if-else,代码也会更加优雅。
8. 易错点与边界值
在实际工程中,标量函数有一些常见的坑需要注意:
浮点精度
// 永远不要用 == 比较两个浮点数
if (Mathf.Abs(a - b) < 0.001f) { /* 近似相等 */ }Lerp 的 “芝诺悖论” 误用
// 错误:永远到不了目标(每帧移动剩余距离的一部分)
pos = Mathf.Lerp(pos, target, Time.deltaTime * speed);
// 正确做法之一:记录已过时间,用归一化 t
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
pos = Mathf.Lerp(startPos, target, t);第一种写法并不是”错”,它能产生自然的减速效果,但如果你需要在固定时间内到达目标,必须用第二种写法。
SmoothStep vs SmootherStep
SmoothStep 的导数在 \(t=0\) 和 \(t=1\)
处为零,但二阶导数不为零——在某些动画场景下仍能看到”小顿挫”。如果需要更丝滑的过渡,可以用
SmootherStep(Ken Perlin 提出):
\[ t = 6t^5 - 15t^4 + 10t^3 \]
除零防御
InverseLerp 和 Remap 在
a == b(分母为零)时会产生
NaN。实际项目中请务必加保护:
float SafeInverseLerp(float a, float b, float v) {
if (Mathf.Approximately(a, b)) return 0f;
return (v - a) / (b - a);
}同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【游戏中的数学】游戏中的数学 (2) - 基础工具箱:三角函数
深入浅出地讲解游戏开发中的三角函数:Sin, Cos, Atan2。从单位圆原理到圆周运动、波浪动画和朝向计算的实际应用。
【游戏中的数学】游戏中的数学 (4) - 矩阵与变换
深入理解游戏开发中的矩阵:TRS 变换与代码实践、矩阵乘法与组合变换、MVP 管线详解、逆矩阵、法线变换,以及旋转表示方式对比。
【游戏中的数学】游戏中的数学 (5) - 四元数
为什么游戏引擎都用四元数来表示旋转?详解欧拉角的万向节死锁问题,以及四元数的定义、运算和 Slerp 插值。
【游戏中的数学】游戏中的数学 (3) - 向量基础
游戏开发数学系列第一篇:向量。介绍向量的定义、加减法、标量乘法、点积与叉积及其在游戏中的实际应用。