很多时候在Unity的一些场景中中会用到平滑插值。比如我有一个Animator状态树,动画通过一个float数值来控制。但是数值是瞬间变化的,动画如果也是瞬间切换,没有过渡效果的话,看着就会很生硬。所以就需要对float进行线性平滑插值。
Mathf.Lerp
Unity官方是有线性平滑插值的函数的,即Lerp函数
Unity中有很多数据都可以通过Lerp来进行线性平滑插值,不过我这次只打算说一下Mathf.Lerp。
其实所有Lerp函数都大差不差,先来看Mathf.Lerp的函数
public static float Lerp(float a, float b, float t);
Mathf.Lerp由三个参数构成,函数最终会返回一个float浮点数,大小是a与b之间关于t的浮点插值结果。t不要大于1,因为t被限制在了0-1之间。
但其实原理很简单
t = 0, return a
t = 0.5, return (a + b) / 2
t = 1, return b
所以当给定了目标数值a,最终结果b后,Lerp函数会根据当前t的大小来返回一个浮点数。所以,如果我们想要实现一个平均且匀速的线性平滑插值,那我们必须保证t是以一个平均的速度从0开始增长到1的。
t更像是一个百分比数(比如,当t = 0.5时,就相当于长度的一半,如果a = 0,b = 5,则相当于长度5的一半,返回2.5),ab之间的长度通过这个百分比数找到一个点,然后将这个点的数值返回。
Unity中的使用
众所周知,Unity生命周期中有两个比较重要的函数,分别是Update和FixedUpdate
Update是每帧便执行一次,FixedUpdate则是有一个固定的数值(一般是0.02s,可以修改),每隔固定的时间执行一次。
先从容易理解的FixedUpdate来说,可以理解为0.02s执行一次。因为是固定速率执行,所以相应的时间增量Time.fixedDeltaTime就等于0.02。
而匀速平滑插值就要保证t是匀速增长的,所以给定一个变量,然后每次FixedUpdate去增加0.02直至等于我们想要的时间,比如1s。最后将这个变量与我们想要时间做一个比值,就可以当成匀速变化的t来用了。
下面是代码(FixedUpdate模式)
public float fixedTime = 0.0f;
private float target = 5.0f;
private float fixedDeltaTime = 0.0f;
private void FixedUpdate()
{
fixedTime = Mathf.Lerp(fixedTime, target, Time.fixedDeltaTime / (target - fixedDeltaTime));
fixedDeltaTime += Time.fixedDeltaTime;
Debug.Log(string.Format("currentTime:{0}", fixedTime));
}
需要注意的是,数值变化的过程中,“距离”target应该是越来越近的,所以后面的速度应该是,目标时间减去已经经过的时间,然后用时间增量deltaTime去做比值。结果就是当前时间增量中应该变化的数值占总数值的比率。
Update同理,只需要把Time.fixedDeltaTime换成Time.deltaTime就可以了
最后Log证明一下确实是匀速运动的
从44秒到45秒正好过了1.0