Unity3D

Unity中Lerp与SmoothDamp函数使用误区浅析

页面
字体
小树 · 11月13日 · 2019年 · ·

引言

Unity的LerpSmoothDamp这两个函数我们在处理物体移动、一些渐进变化或者摄像机的跟随等场景会较常用到,虽然MathfVector2Vector3等都有这两个函数,但用法上是基本一样的,用起来也比较简单。不过在实际的运用中,往往一不小心却会掉进坑里,下面我们一起来分析一下。

Lerp插值函数

Lerp函数使用的最常见的误区是把线性移动用成了弹性移动

(关键是自己还不知道,还奇怪,咦,不是说好的线性移动吗?怎么有点弹性的赶脚……),如下图所示:

Lerp 一般错误的使用方法如下:

    private Vector3 target = new Vector3(0, 0, 5);
 
    void Update()
    {
            transform.position = Vector3.Lerp(transform.position, target, 0.1f);
    }

这个错误主要是没有理解好Lerp的第三个参数t的作用,

这里我们为了便于理解,我们拿Mathf.Lerp函数来分析,思路是一样的。

我们先来看一下Mathf.Lerp函数的具体实现:

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

估计大家一看函数的实现就明白了关键点,想要线性移动,应该只控制t的变化,t的值在0-1,它就类似一个百分比的值,代表着a点到b点之间的位置,比如想要到a和b之间的3/10的位置,t就应该是0.3,想要一步到位t的值就应该为1。在上述错误的用法中,实际的效果就是第一次到达1/10位置,第二次到达1/10 9/10*1/10的位置……理论上讲永远到不了b点,每一次都是a与b点之间的1/10的位置,所以移动的幅度越来越小,有一种弹性感觉。

Lerp 正确的使用方法如下:

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);
}

正确使用方法的效果如下:

SmoothDamp函数

SmoothDamp函数在使用过程中比较容易出现的一个问题:

就是容易在代码较复杂的情形下将currentVelocity这个参数需要的变量定义为局部变量,如下:

SmoothDamp 一般错误的使用方法如下:

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

使用局部变量的效果如下图所示,越来越慢,定义的平滑时间明明是0.3f,怎么用了10几秒的时间都还在缓慢移动?

为了便于理解,我们来看一下Mathf.SmoothDamp的具体实现:

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;
        }

通过具体的实现我们就可以很清楚的发现,currentVelocity 这个变量是先使用,然后再赋值,通过ref传递就是为了获取到上一次计算得到的速度值,如果我们使用局部变量,每一次速度都是零,相当于刚开始进行平滑移动,平滑的距离(transform.position到target)不断在缩短,然而平滑时间却没有变化,为了保证大约在平滑的时间内完成平滑移动,这个起步速度肯定是越来越慢的,所以就导致了上图中的问题。 我们把currentVelocity改为全局变量,就可以看到正常效果了

SmoothDamp 正确的使用方法如下:

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);
    }

正确效果如下:

转载来源:http://www.manew.com/thread-111917-1-1.html

转载必须注明来源: 小树技术博客 » Unity中Lerp与SmoothDamp函数使用误区浅析

0 条回应