很多時候在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