【算法分享】真的有那么丝滑吗——摄像机平滑追踪人物算法

平滑追踪
这是我在做摄像机平滑追踪移动人物时发现的
简单、快捷、实用,仅7行代码(TypeScript)

实现原理
摄像机离人物越近,追踪速度越慢;摄像机离人物越远,追踪速度越快。一种距离,对应一种速度。
如果当前速度 ≠ 当前距离对应的速度,则向当前距离对应的速度靠拢

代码解释

k是一个效果常数,
0 < k < 1:摄像机追踪人物,剧烈地来回抖动,且抖动幅度越来越大
k = 1:摄像机追踪人物,来回抖动,且无衰减;
1 < k < 2:随着k值增大,摄像机追踪人物,抖动逐渐消失,摄像机移动速度逐渐减小
k>=2:摄像机追踪人物,完全没有抖动,但摄像机移动速度较小
考虑到抖动程度较小(其实轻微抖动不是什么坏事,反而有独特的效果),并且摄像机移动速度不太慢,此处k取1.7

this.node是摄像机,this.player.node是人物
dx、dy就分别是x轴、y轴上摄像机与人物的距离差
为了实现 “摄像机离人物越近,追踪速度越慢;摄像机离人物越远,追踪速度越快”,就直接用距离代替速度了(这里其实还可以自己设计一个单增函数),即dx、dy表示当前距离对应的速度。
于是第4、5行就是平滑地改变摄像机的速度,向着目标速度的方向
最后两行就是更新位移了

另外,这个算法除了可以用于摄像机追踪人物之外,还可以用于其他物体追踪的例子:smiley::smiley::smile:

8赞

直接用插值它不香吗?

我是在update中计算坐标然后步进1/10距离,也能平滑的跟随
//玩家坐标 let posPlayer=this.playerNode.convertToWorldSpaceAR(cc.Vec2.ZERO) let posCamera=this.node.convertToWorldSpaceAR(cc.Vec2.ZERO) //计算偏移 let between=posPlayer.sub(posCamera) let betweenLength=between.mag() let betweenOne=between.normalize() //步进 betweenLength/=10 //跟进 let posMove=posCamera.add(betweenOne.mul(betweenLength)) this.node.position=cc.v2(this.node.parent.convertToNodeSpaceAR(posMove))

1赞

let x: number = cc.misc.lerp(startX, targetX, dt * k);试试这个会不会好使
感谢分享:cherry_blossom:

4赞

:heart_eyes:看来如果仅仅是跟随的话,这么写就好了。很简洁,受教了:smiley:
那我可以挖掘一下这个算法做出的抖动的效果

如1楼所说 cc.misc.lerp 才是最方便的方法

正解,补充下,所有 ValueType 都有 lerp 函数

posA.lerp(posB, dt * k)

2赞

lerp函数的实现原理其实是和这个差不多的

 /**
     * !#zh 逐元素向量线性插值: A + t * (B - A)
     * @method lerp
     * @typescript
     * lerp <Out extends IVec2Like> (out: Out, a: Out, b: Out, t: number): Out
     * @static
     */
    static lerp <Out extends IVec2Like> (out: Out, a: Out, b: Out, t: number) {
        _x = a.x;
        _y = a.y;
        out.x = _x + t * (b.x - _x);
        out.y = _y + t * (b.y - _y);
        return out;
    }