Unity中Lerp與SmoothDamp函數(shù)使用誤區(qū)淺析

Lerp函數(shù)

Lerp函數(shù)使用的最常見的誤區(qū)是把線性移動用成了彈性移動(關(guān)鍵是自己還不知道,還奇怪,咦,不是說好的線性移動嗎?怎么有點彈性的趕腳……),如下圖所示:


image
 
void Update()
    {
            transform.position = Vector3.Lerp(transform.position, target, 0.1f);
    }

這個錯誤主要是沒有理解好Lerp的第三個參數(shù)t的作用,這里我們?yōu)榱吮阌诶斫?,我們拿Mathf.Lerp函數(shù)來分析,思路是一樣的。我們先來看一下Mathf.Lerp函數(shù)的具體實現(xiàn):

/// <summary>
///   <para>Clamps value between 0 and 1 and returns value.</para>
/// </summary>
/// <param name="value"></param>
public static float Clamp01(float value)
{
    float result;
    if (value < 0f)
    {
        result = 0f;
    }
    else if (value > 1f)
    {
        result = 1f;
    }
    else
    {
        result = value;
    }
    return result;
}
 
/// <summary>
///   <para>Linearly interpolates between a and b by t.</para>
/// </summary>
/// <param name="a">The start value.</param>
/// <param name="b">The end value.</param>
/// <param name="t">The interpolation value between the two floats.</param>
/// <returns>
///   <para>The interpolated float result between the two float values.</para>
/// </returns>
public static float Lerp(float a, float b, float t)
{
    return a + (b - a) * Mathf.Clamp01(t);
} 

估計大家一看函數(shù)的實現(xiàn)就明白了關(guān)鍵點,想要線性移動,應(yīng)該只控制t的變化,t的值在0-1,它就類似一個百分比的值,代表著a點到b點之間的位置,比如想要到a和b之間的3/10的位置,t就應(yīng)該是0.3,想要一步到位t的值就應(yīng)該為1。在上述錯誤的用法中,實際的效果就是第一次到達1/10位置,第二次到達1/10+9/10*1/10的位置……理論上講永遠到不了b點,每一次都是a與b點之間的1/10的位置,所以移動的幅度越來越小,有一種彈性感覺。正確的使用方法如下:

private Vector3 target = new Vector3(0, 0, 5);
private Vector3 startPos;
private float t1;
 
void Start()
{
    startPos = transform.position;
}
 
void Update()
{           
        t1 += 1f * Time.deltaTime;
        transform.position = Vector3.Lerp(startPos, target, t1);
}
image

SmoothDamp函數(shù)

SmoothDamp函數(shù)在使用過程中比較容易出現(xiàn)的一個問題就是容易在代碼較復(fù)雜的情形下將currentVelocity這個參數(shù)需要的變量定義為局部變量,如下:

 public float smoothTime = 0.3F;
 
 void Update()
 {
         Vector3 velocity = Vector3.zero;
         transform.position = Vector3.SmoothDamp(transform.position, target, ref velocity, smoothTime);
     }
 }

使用局部變量的效果如下圖所示,越來越慢,定義的平滑時間明明是0.3f,怎么用了10幾秒的時間都還在緩慢移動?
image

為了便于理解,我們來看一下Mathf.SmoothDamp的具體實現(xiàn):

public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, [DefaultValue("Mathf.Infinity")] float maxSpeed, [DefaultValue("Time.deltaTime")] float deltaTime)
        {
            smoothTime = Mathf.Max(0.0001f, smoothTime);
            float num = 2f / smoothTime;
            float num2 = num * deltaTime;
            float num3 = 1f / (1f + num2 + 0.48f * num2 * num2 + 0.235f * num2 * num2 * num2);
            float num4 = current - target;
            float num5 = target;
            float num6 = maxSpeed * smoothTime;
            num4 = Mathf.Clamp(num4, -num6, num6);
            target = current - num4;
            float num7 = (currentVelocity + num * num4) * deltaTime;
            currentVelocity = (currentVelocity - num * num7) * num3;
            float num8 = target + (num4 + num7) * num3;
            if (num5 - current > 0f == num8 > num5)
            {
                num8 = num5;
                currentVelocity = (num8 - num5) / deltaTime;
            }
            return num8;
        }

通過具體的實現(xiàn)我們就可以很清楚的發(fā)現(xiàn),currentVelocity這個變量是先使用,然后再賦值,通過ref傳遞就是為了獲取到上一次計算得到的速度值,如果我們使用局部變量,每一次速度都是零,相當于剛開始進行平滑移動,平滑的距離(transform.position到target)不斷在縮短,然而平滑時間卻沒有變化,為了保證大約在平滑的時間內(nèi)完成平滑移動,這個起步速度肯定是越來越慢的,所以就導(dǎo)致了上圖中的問題。
我們把currentVelocity改為全局變量,就可以看到正常效果了

private Vector3 target = new Vector3(0, 0, 5);
    public float smoothTime = 0.3F;
    private Vector3 velocity = Vector3.zero;
    void Update()
    {
            transform.position = Vector3.SmoothDamp(transform.position, target, ref velocity, smoothTime);
    }

效果如下:


image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容