SmoothDamp不能用std::lerp替代,因其本质是模拟带阻尼与加速度限制的物理运动,目标越近速度越慢、自然停驻且不超调;而lerp为恒定速率线性插值,帧率波动时易卡顿突兀,且无法保持速度连续性。

SmoothDamp 为什么不能直接用 std::lerp 替代
因为 std::lerp 是线性插值,速度恒定;而 SmoothDamp 模拟的是带加速度限制和阻尼的物理运动——目标越近,速度越慢,最终自然停住,不震荡、不超调。直接用 std::lerp 做“缓动”会卡顿、突兀,尤其在帧率波动时更明显。
Unity 的 Mathf.SmoothDamp 是经典实现,C++ 中没有标准等价物,必须手写。核心难点不在公式,而在:如何让时间步长 deltaTime 参与积分、如何防止数值发散、如何处理目标突变。
手写 SmoothDamp 的关键三步(带防错逻辑)
参考 Unity 原版思路,但 C++ 实现需显式管理状态变量(当前速度、上一帧目标值),并做边界防护:
- 始终用
currentVelocity作为独立状态变量传入/传出,不能每次重置为 0 - 目标值突变时(如玩家瞬移),要允许
currentVelocity瞬间“重定向”,但幅度受maxSpeed限制 - 时间步长
deltaTime必须参与指数衰减计算,不能简单乘系数;推荐用exp(-k * deltaTime)形式,避免大 dt 下过冲 - 当
deltaTime接近 0(如暂停或首帧),要跳过速度更新,否则1/deltaTime类操作会崩溃
简化的工业级可用版本(无模板、无异常、兼容 float/double):
float SmoothDamp(float current, float target, float& currentVelocity, float smoothTime, float deltaTime, float maxSpeed = FLT_MAX) {
const float omega = 2.0f / smoothTime;
const float x = omega * deltaTime;
const float expTerm = 1.0f / (1.0f + x + 0.48f * x * x + 0.235f * x * x * x); // 近似 exp(-omega * dt)
<pre class="brush:php;toolbar:false;">float delta = current - target;
float temp = (currentVelocity + omega * delta) * deltaTime;
currentVelocity = (currentVelocity - omega * temp) * expTerm;
float output = target + delta * expTerm + temp * expTerm;
// 限速:只约束速度增量,不粗暴截断 currentVelocity(否则破坏阻尼连续性)
const float maxDelta = maxSpeed * deltaTime;
const float clampedDelta = std::clamp(output - current, -maxDelta, maxDelta);
output = current + clampedDelta;
return output;}
smoothTime 参数的实际含义和调参陷阱
smoothTime 不是“持续时间”,而是系统时间常数(单位:秒)——它约等于从初速度衰减到 1/e ≈ 37% 所需的时间。数值越小响应越快,但过小会导致抖动或超调;过大则拖沓。
- 典型值范围:0.05 ~ 0.3 秒(UI 动画常用 0.15,相机跟随常用 0.2~0.25)
- 别把
smoothTime设成 0 —— 会除零或导致 expTerm = 1,退化为无阻尼惯性 - 如果发现目标变化后“滞后严重”,优先调小
smoothTime,而不是增大maxSpeed;后者只解决末端爬行,不改善响应延迟 - 多对象共用同一
smoothTime时,注意不同量纲(比如旋转用弧度、位移用米)需按比例缩放,否则阻尼感不一致
和 Lerp 混用时最容易崩的两个点
有人想“先 Lerp 快接近,再 SmoothDamp 细调”,这在数学上不自洽,实践中极易出问题:
-
currentVelocity状态无法从 Lerp 结果中反推 —— 你不知道上一帧速度是多少,强行传入 0 会导致下一次调用突加速 - 若在 SmoothDamp 中途切回 Lerp,再切回来,
currentVelocity已失真,后续运动轨迹完全失控(常见于 UI 状态切换)
正确做法:全程只用 SmoothDamp,靠动态调整 smoothTime 或 maxSpeed 控制快慢。例如鼠标悬停时设 smoothTime = 0.05,移开时设 smoothTime = 0.2,比混合两种算法干净得多。
真正难的不是公式,是把 currentVelocity 当作一等公民维护好——它得跨帧存在、能被外部安全读写、在 reset 时有明确语义。漏掉这点,再准的数学也没用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











