Cocos 3.x Vec3.lerp插值

2021-10-21  本文已影响0人  合肥黑

Unity3D中的线性插值Lerp()函数解析
Cocos Creator 3D 打砖块教程(二) | 子弹发射与摄像机平滑移动

在组件的每帧事件 update 中真正控制摄像机节点的移动。为了平滑移动,Shawn这里参考了官方Demo案例中的做法,使用 Vec3.lerp 对当前坐标到要移动的坐标进行插值计算。

update (deltaTime: number) { 
    //计算要移动的目标位置
    Vec3.add(this.point, this.node.position, this.offset);
    //插值计算
    Vec3.lerp(this.point, this.node.position, this.point, deltaTime * this.speed);
    //移动节点
    this.node.setPosition(this.point);
}

delta time。 间隔时间的意思。粗略意思是 该update距离上次update的时间

image.png
/**
 * @en Element-wise vector addition and save the results to out vector object
 * @zh 逐元素向量加法
 */
static add<Out extends IVec3Like>(out: Out, a: IVec3Like, b: IVec3Like): Out;

/**
 * @en Calculates the linear interpolation between two vectors with a given ratio
 * @zh 逐元素向量线性插值: A + t * (B - A)
 */
static lerp<Out extends IVec3Like>(out: Out, a: IVec3Like, b: IVec3Like, t: number): Out;

export function lerp(from, to, ratio) {
  return from + (to - from) * ratio;
}

参考 有做大型RPG或SLG游戏需求的朋友请进,有在线地图编辑器

Q:地图移动用的tween么? 居然不卡顿
A:从a点走到b的过程中突然又点击了c点,a到b点的行走被打断,改为当前位置到c点行走,用tween不好控制,而且tween性能消耗大。而插值可以轻松每时每刻的屏幕平滑移动。还有一种情况,arpg游戏,a点走到b点的过程中,角色中了一个晕眩技能,被迫停下,用tween就不好处理了
Q:哦对对,这个时候要立马停下从当前坐标 走到下一个节点的。 问下插值这个东西保证每一“步进值”都一样么?应该有小数点的呀,地图移动过程中,如果有小数点也会有抖动的(之前页游是这样的),要么提高帧率到60,,
A:插值范围是从0到1,原理是从角色当前位置到目标位置距离的百分比行进,不会产生抖动。如果你搞过unity就知道了,插值用得很频繁的


image.png

Q:我看了看api对比你的源码,他这个线性插值是目标点与当前点差,乘以一个缩放因子t, 这个t呢又是update的每帧的间隔时间,如果游戏流畅不卡顿的话 ,帧间隔时间比较稳定,极端情况下掉帧的话, 地图移动速度就会“忽快忽慢"了,不知道我理解的有诶有问题哎,
A:单位时间内update的dt是一定的,就算网络卡,也不会忽快忽慢。乘t就是为了保证匀速运动

参考Lerp 实现匀速运动
Lerp的常见“误用”是

Update()
{
    Transform.position = Vector3.Lerp(transform.position.x, targetPosition, Time.deltaTime);
}

说是“误用”,其实也不完全正确,这种用法是可以工作的,但是常常不是大家的真正需求,很多时候大家使用Lerp都是想达到匀速运动的效果,但如下“误用”却让对象以逐渐降低的速度运动。

首先,上述“误用”是这样工作的:每帧都重新获取物体当前的位置,计算物体和目标距离的差距,再按照当前帧的持续时间(当做一个百分比)来移动这个比例的位置。因此如果目标位置始终是固定的,那么整体运动是缓动的,先快后慢。这样的效果乍一看还不错,但其实是有一些问题的:因为每秒钟都以固定的比例靠近目标位置,所以运动速度会以固定的比例逐渐降低,只要运算精度够高,运动永远达不到目标,且运算始终在进行。如果确实需要这样做,那么我们需要加上一个阈值,当与目标距离小于这个阈值时,就直接把物体的位置设置为目标位置。这个阈值的大小设置要合适,太大了在后面阶段会感觉到明显的跳跃,太小了会浪费运算时间。

这里说明一下,如果上述效果就是我们想达到的目标,那么用Time.deltaTime作为第三个参数在这个情况下是有道理的,因为每帧时间不同,为了保证单位时间内运动的百分比是一致的(达到平滑缓动的效果),需要用Time.deltTime介入。

如果你是误打误撞实现了缓动效果,并且觉得效果不错就没有再深究了,那么建议你继续往下看看。

float speed = 2.0f;

//什么时候开始运动
float startTime = 2.0f;

//起始X位置
float startX = 0.0f;

//结束X位置
float endX = 0.0f;

void Update()
{
    float lerpValue = Mathf.Lerp(startX,endX,(Time.time-startTime )* speed);
    transform.position = new Vector3(lerpValue,0,0);
}

一定要理解清楚Mathf.Lerp(float a, float b, float t)第三个参数t的意义,它是一个百分比,最小值有效值是0,最大有效值是1,如果超出了1,就取1,小于0则取0.

它表示从a到b之间,按照t这个百分比来取值,例如a是0,b是100,如果t是0.2,则该函数返回的值是20,如果t是1,该函数返回的值为100.

匀速运动的要点是起始值和结束值都是固定好的,不会随着运动而发生变化。

Time.time就是系统运行时间,也就是这个程序开始到现在的时长。

(Time.time - startTime),上面例子中startTime是2.0f,那么这个式子的取值一开始是-2,2秒时变成0,3秒时变成1,先假设没有乘以speed这个值,整个运动过程会在2秒开始,3秒结束。

物体运动的速度是距离差(在本例中是10.0f)除以1秒。乘以一个speed以后,实际上是在调整整体的运动时间。
假设speed为0.1f,则运动的时间变为2秒开始12秒结束,运行时间变成了10,则速度变成原先的1/10,
同理,假设speed 为10f,则运动时间变为2秒开始2.1秒结束,速度变成原先的10倍。

上一篇下一篇

猜你喜欢

热点阅读