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

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

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函数的具体实现:
/// <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);
} 


估计大家一看函数的实现就明白了关键点,想要线性移动,应该只控制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的位置,所以移动的幅度越来越小,有一种弹性感觉。正确的使用方法如下:
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这个参数需要的变量定义为局部变量,如下:
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改为全局变量,就可以看到正常效果了
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);
    }


效果如下:
 

04.gif (26.08 KB, 下载次数: 0)

下载附件  保存到相册

昨天 20:48 上传




写在最后
好了,以上就是这一次想要和大家一起讨论的内容了,希望对大家有那么一丢丢的帮助。

本文工程文件(Unity2017.2.0f3):

链接:https://pan.baidu.com/s/1kV7rd4b  密码:vft2

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值