Unity Mathf的数值平滑方法
Unity 的 Mathf 类中,用于 数值平滑(渐变、缓冲、缓动) 的核心方法有 5 个,覆盖不同平滑场景(线性渐变、带减速缓冲、缓入缓出、往返平滑等),以下是完整整理,含 方法功能、适用场景、可运行示例,方便直接套用:
一、核心数值平滑方法(按常用度排序)
1. Mathf.SmoothDamp(平滑阻尼,最常用)
功能
带 自然减速效果 的平滑趋近目标值,通过速度变量(引用类型)模拟惯性,最终平稳停在目标值,不会超调。
关键特性
- 有“缓冲感”,适合需要自然减速的场景(如相机跟随、数值缓冲);
- 需持续在
Update中调用,速度变量需提前初始化且作为ref参数传入。
示例代码(相机平滑跟随目标X轴)
public Transform target; // 跟随目标
private float currentX;
private float velocity = 0f; // 速度变量(必须初始化)
public float smoothTime = 0.3f; // 平滑时间(越小越快)
void Start()
{
currentX = transform.position.x;
}
void Update()
{
// 平滑阻尼趋近目标X坐标
currentX = Mathf.SmoothDamp(currentX, target.position.x, ref velocity, smoothTime);
// 应用到相机位置
transform.position = new Vector3(currentX, transform.position.y, transform.position.z);
}
适用场景
相机跟随、UI元素平滑移动、血条/音量等数值缓冲、物体惯性运动。
2. Mathf.SmoothStep(缓入缓出平滑)
功能
在 from 和 to 之间做 S形曲线过渡(缓入+缓出),t 在 0~1 范围内时,数值先慢后快再慢,比线性插值更自然。
关键特性
- 无需持续调用,只要
t从 0 渐变到 1 即可触发平滑效果; t超出 0~1 时,结果会紧贴from或to(不会外推)。
示例代码(UI按钮缩放动画)
public Transform btnTransform;
public float duration = 1f; // 动画时长
void Update()
{
// t 从 0 循环到 1(配合PingPong实现往返)
float t = Mathf.PingPong(Time.time / duration, 1f);
// 缓入缓出缩放(0.8→1.2→0.8)
float scale = Mathf.SmoothStep(0.8f, 1.2f, t);
btnTransform.localScale = Vector3.one * scale;
}
适用场景
UI动画(缩放、位移、透明度)、物体缓动效果、平滑过渡的参数调整。
3. Mathf.Lerp(线性插值,基础平滑)
功能
在 a 和 b 之间做 线性渐变(匀速过渡),t 为 0 时返回 a,t 为 1 时返回 b,中间值匀速变化。
关键特性
- 平滑效果均匀,但无减速,适合需要匀速渐变的场景;
- 需持续在
Update中调用,通过t = speed * Time.deltaTime控制渐变速度(帧率无关)。
示例代码(音量平滑调节)
public AudioSource audioSource;
public float targetVolume = 0.7f;
public float smoothSpeed = 1f;
void Update()
{
// 线性插值平滑调节音量
float currentVolume = Mathf.Lerp(audioSource.volume, targetVolume, smoothSpeed * Time.deltaTime);
audioSource.volume = currentVolume;
// 接近目标时停止打印(避免无限循环)
if (Mathf.Approximately(currentVolume, targetVolume))
{
Debug.Log("音量调节完成");
enabled = false;
}
}
适用场景
匀速数值过渡、简单移动渐变、参数线性调整(如亮度、透明度)。
4. Mathf.MoveTowards(匀速趋近,无超调)
功能
匀速逼近目标值,每次移动的最大步长为 maxDelta,到达目标后直接停止(不会超调),本质是“限制步长的线性渐变”。
关键特性
- 比
Lerp更精准控制移动速度,适合需要明确“每秒移动X单位”的场景; - 无需手动处理目标边界,到达后自动停住。
示例代码(物体匀速平滑移动到目标点)
public Transform target;
public float moveSpeed = 3f; // 每秒移动3个单位
private Vector3 currentPos;
void Start()
{
currentPos = transform.position;
}
void Update()
{
// 匀速趋近目标X坐标(Y、Z保持不变)
currentPos.x = Mathf.MoveTowards(currentPos.x, target.position.x, moveSpeed * Time.deltaTime);
transform.position = currentPos;
// 到达目标时提示
if (Mathf.Approximately(currentPos.x, target.position.x))
{
Debug.Log("移动完成");
enabled = false;
}
}
适用场景
角色匀速移动、物体追击、数值匀速变化(如倒计时、进度条填充)。
5. Mathf.PingPong + 平滑方法(往返平滑)
功能
PingPong 本身是“往返循环”方法(0→length→0),配合 Lerp/SmoothStep 可实现 往返平滑过渡(如呼吸灯、摇摆动画)。
关键特性
- 无需手动控制“往返方向”,
PingPong自动循环t值; - 结合
SmoothStep可实现往返缓入缓出,效果更自然。
示例代码(呼吸灯效果)
public Light pointLight;
public float duration = 2f; // 一个往返周期(亮→暗→亮)
void Update()
{
// t 从 0 循环到 1(PingPong 控制往返)
float t = Mathf.PingPong(Time.time / duration, 1f);
// 结合 SmoothStep 实现缓入缓出的亮度变化(0.5→2→0.5)
float intensity = Mathf.SmoothStep(0.5f, 2f, t);
pointLight.intensity = intensity;
}
适用场景
呼吸灯、物体往返摇摆、UI元素闪烁、数值周期性平滑波动。
二、角度专用平滑方法(数值平滑的衍生场景)
如果需要平滑处理 角度旋转(如角色转向、相机旋转),Mathf 提供了 3 个角度专用平滑方法,自动处理 360° 边界问题(避免绕远路):
1. Mathf.SmoothDampAngle(角度平滑阻尼)
public float targetAngle = 90f;
private float currentAngle = 0f;
private float velocity = 0f;
public float smoothTime = 0.2f;
void Update()
{
// 平滑阻尼旋转(自动走最短路径)
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref velocity, smoothTime);
transform.rotation = Quaternion.Euler(0, currentAngle, 0);
}
2. Mathf.LerpAngle(角度线性插值)
public float targetAngle = 180f;
private float currentAngle = 0f;
void Update()
{
// 角度线性平滑(自动处理360°边界)
currentAngle = Mathf.LerpAngle(currentAngle, targetAngle, 0.5f * Time.deltaTime);
transform.rotation = Quaternion.Euler(0, currentAngle, 0);
}
3. Mathf.MoveTowardsAngle(角度匀速趋近)
public float targetAngle = 270f;
private float currentAngle = 0f;
public float rotateSpeed = 60f; // 每秒转60度
void Update()
{
// 角度匀速旋转(自动走最短路径)
currentAngle = Mathf.MoveTowardsAngle(currentAngle, targetAngle, rotateSpeed * Time.deltaTime);
transform.rotation = Quaternion.Euler(0, currentAngle, 0);
}
三、平滑方法选择指南(避免踩坑)
| 需求场景 | 推荐方法 | 核心优势 |
|---|---|---|
| 带减速的自然平滑(如相机跟随) | Mathf.SmoothDamp | 有惯性,效果自然,无超调 |
| 缓入缓出的动画(如UI缩放) | Mathf.SmoothStep | S形曲线,比Lerp更柔和 |
| 匀速渐变(如音量调节) | Mathf.Lerp | 简单直接,适合基础平滑需求 |
| 精准控制速度(如角色移动) | Mathf.MoveTowards | 明确步长,到达目标自动停止 |
| 往返平滑(如呼吸灯) | Mathf.PingPong + SmoothStep | 自动循环,无需手动控制方向 |
| 角度旋转(如角色转向) | SmoothDampAngle/LerpAngle | 自动处理360°边界,避免绕远路 |
四、关键使用注意事项
- 持续调用要求:
SmoothDamp/Lerp/MoveTowards需在Update/FixedUpdate中持续调用,单次调用仅返回当前插值结果; - 速度变量初始化:
SmoothDamp/SmoothDampAngle的velocity参数必须初始化(如float velocity = 0f),且是ref类型(不能直接传字面量); - 帧率无关:所有平滑方法的“速度/步长”需乘以
Time.deltaTime,确保不同帧率下平滑效果一致; - 边界处理:角度平滑优先用专用方法(如
LerpAngle),避免手动计算角度差导致的绕路问题; - 停止条件:用
Mathf.Approximately判断是否到达目标值(避免浮点数精度误差),防止无限循环。
这些方法覆盖了 Unity 开发中 95% 以上的数值平滑场景,结合使用可实现自然、流畅的动画和数值过渡效果。