引言
在打开 Unity 的 Mathf 或 Unreal 的
FMath
文档时,你可能会看到几十个函数。其中有一些函数是如此常用,以至于它们构成了游戏逻辑的基石。
本章不讨论复杂的向量或矩阵,只关注最基础的标量
(Scalar) 运算——即对单个浮点数的操作。
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。csharp // 每秒移动 5 米,直到到达 target currentPos = Vector3.MoveTowards(currentPos, target, 5.0f * Time.deltaTime);
总结
| 函数 | 作用 | 典型应用 |
|---|---|---|
| Lerp | 线性插值 (按比例) | 平滑跟随、颜色渐变 |
| InverseLerp | 求比例 | 计算进度条、归一化数据 |
| Remap | 范围转换 | 数值映射、UI适配 |
| Clamp | 限制范围 | 血量限制、角度限制 |
| SmoothStep | S形插值 | 自然动画、地形平滑 |
| MoveTowards | 匀速接近 | 匀速移动、巡逻 |
掌握这些基础函数,能让你在写游戏逻辑时少写很多
if-else,代码也会更加优雅。