前言
相信做unity的同学或多或少都会有对数值变化进行平滑的需求,比如摄像机的移动,物体颜色的渐变等等,我们不想变化太突然,想让变化过度自然,这个时候我们很自然的会用到Lerp()函数或者SmoothDamp()函数(当然用协程也可以实现),但是我最初使用这两个函数的时候,用出来是这个效果,但是确实不是很理解里面参数所代表的意义,后面还是花了一点点的时间才彻底的清楚这个函数的用法,在这篇文章里我会尝试清晰的把这两个函数一次性讲清楚。(不得不吐槽一下,unity官方文档最初对Lerp函数用法的示例!将
Time.deltatime
加入第三个参数太不利于理解了!)
1.Lerp解析
1.1Lerp原型以及文字释义
在unityAPI中有各种各样的Lerp可供选择什么Color.Lerp
,Vector2.Lerp
,Vector3.Lerp
等等,网上有些教程把他们一个个拿出来解释,我觉得真的是毫无意义的,最关键的只需要理解其中的一个Lerp就足够了,其它Lerp只是针对它的每一个属性去做变化而已,本质是一样的。
那就是
Mathf.Lerp()
//a:变化的起始值
//b:变化的目标值
//t:本次调用返回值所占b和a的差值的比例
//返回值为一个float类型
public static float Lerp(float a, float b, float t);
1.2Lerp初体验
我们拿到一个函数,按照常理去理解,应该调用一次就ok了,效果就出来了,那么我们就在Start()
里面调用一次试一下
float origin_X = 0;//起始X坐标
float destination_X = 10;
// Use this for initialization
void Start()
{
//修改正方体在x轴上面的位移
Vector3 startPos = transform.position;
startPos.x = Mathf.Lerp(origin_X, destination_X, 0.1f);
transform.position = startPos;
}
效果如下,其中蓝色的正方体所在坐标为(0,0,0),红色正方体所在坐标为(10,0,0)
我们会发现白色正方体所在的位置变为了(1,0,0),刚好就是起始点x:0和
终点x:10的十分之一,也是刚好是
Math.Lerp()
的第三个参数0.1。所以这很直观的让我们知道了第三个参数表征的是什么
第三个参数表征的是,函数的返回值占前两个参数插值的比例,附上网上流传的Lerp实现源码大家就会发现是真的简洁明了
public static float Lerp(float a, float b, float t){
return (b-a)*t;
}
1.3Lerp的用法
根据1.2我们可以知道一件事,想要实现平滑的效果,调用一次Lerp是不行的,因此我们需要在多次调用,巧妙的修改输入的参数,才能得到我们想要的效果。
1.3.1第一种用法(也是unity早期对Lerp的示例用法)
这种用法会多次调用修改第一个参数的值,接下来我们修改一下代码,将Lerp放在Update()
里面运行
float origin_X = 0;//起始X坐标
float destination_X = 10;
// Update is called once per frame
void Update()
{
//修改正方体在x轴上面的位移
Vector3 startPos = transform.position;
startPos.x = Mathf.Lerp(origin_X, destination_X, 0.1f);
transform.position = startPos;
}
输入代码后点击运行,恐怖的事情发生了!!!我们会发现白色正方体一直在(1,0,0)的位置。检查一下代码发现,我们传入的参数三个值是恒定的,所以每次返回值都是一样的,因此我们每一帧更新白色正方体的position
都会是(1,0,0)。这个时候我们只需要做一件事情,就可以让白色方块从(0,0,0)平滑的运动到(10,0,0),那就是将第一个参数改为自身在当前帧的X坐标,这样一来我们每一帧计算得到的X值都会成为下一帧计算的第一个参数,效果如下。
嗯,so far so good,right?这样去使用的意思是,我们每一帧去移动当前位置距离目标位置的十分之一,也就是
- 第一帧:移动到当前位置(0,0,0)到(10,0,0)的十分之一,也就是(1,0,0)
- 第二帧:移动到当前位置(1,0,0)到(10,0,0)的十分之一,也就是(1.9,0,0)
- 第二帧:移动到当前位置(1.9,0,0)到(10,0,0)的十分之一,也就是(2.71,0,0)
- 后面以此类推......
我们可以很直观的看到,这样子的变化是非线性的(网上有人说Lerp函数的平滑是非线性的,我认为有失偏颇,只能说在这种用法之下,它的变化是非线性的),其次很关键的一点是,像这样子运动,物体永远都到不了你想去的地方,只能无限的去逼近!
这就很坑爹了,很多时候我们想得到变化结束的回调,然后判断
if(position.x==10){TODO......}
然后你发现,这条等式永远不成立,一直是9.9999999,真的急skr人,虽然去我们可以把条件改为
if(position.x>=9.9999999){TODO......}
但这也太......丑了,所以如果我们不想代码那么丑,又想让我们的变化是线性变化的话,就可以采用下面第二种方法。
1.3.2第二种用法(unity现在对Lerp的示例用法)
第二种方法是通过每一帧去改变第三个参数的值,让第三个值从0逐渐变化到1,也就可以实现平滑,并且可以准确的知道变化结束的那一刻了。我这里给出一种最为简单的实现。
float origin_X = 0;//起始X坐标
float destination_X = 10;
float t = 0;//每帧增加的插值
// Update is called once per frame
void Update()
{
t += Time.deltaTime;
//修改正方体在x轴上面的位移
Vector3 startPos = transform.position;
startPos.x = Mathf.Lerp(origin_X, destination_X, t);
transform.position = startPos;
}
其中t每一帧去累加Time.deltaTime,所以从Update第一次调用开始,到1秒的时候t就会变为1,白色方块就会到达终点,所以方块的变化轨迹这时就由t的变化轨迹确定了,在这里t的变化轨迹基本是匀速运动,所以白块也就匀速的从(0,0,0)点运动到(10,0,0)点。
所以在这种情况下很好的可以判断运动什么时候结束,只需要判断t的值是否大于等于1就可以了
if(t>=1){ TODO...... }
最后
本来想简洁明了的解释清楚这个函数,结果还是写了那么多......看来还是要好好锻炼自己对重点的提炼能力了,其中有什么疑惑或者不认同的地方欢迎大家留言!这周末浪的时间太多了,没有写完SmoothDamp的解释,只能下周再更了!