[开发者心得] 【Tween—补间动画】😈用最简洁的代码实现一些动画效果

    [复制链接]
3816 |6
空伊伊 发表于 2023-2-19 10:45:08 | 显示全部楼层 |阅读模式

本帖最后由 空伊伊 于 2023-11-8 18:12 编辑

Tween - 补间动画

本章节需要掌握的内容:Tween的使用方法、数字动画、UI动画、物体动画

1.Tween是什么?

补间(动画)(来自in-between维基百科)是一个概念,允许你以平滑的方式更改对象的属性。你只需告诉它哪些属性要更改,当补间结束运行时它们应该具有哪些最终值,以及这需要多长时间,补间引擎将负责计算从起始点到结束点的值。

说通俗一点,Tween就是一个封装好的函数库,能够实现一些插值运算,方便定义和操作一些补间组。能够使代码的结构看起来更加清晰!

2.使用步骤

2.1.将全局补间组添加到update

Tween本身不会运行,需要通过update对Tween进行驱动,推荐在主循环中添加对应代码,如下:

@Component
export default class GameStart extends Script {
    protected async onStart(): Promise<void> {
        // 注意开启该脚本的update,不然tween的update就也不会执行
        this.useUpdate = true
    }

    protected onUpdate(dt: number): void {
        // 这个大写的TWEEN,是指全局补间组。所有实例化的Tween,都会添加到这个组里,然后统一进行update。所以如果不将全局补间组添加到update,所有补间动画就都不会执行
        TweenUtil.TWEEN.update();
    }
}

2.2.创建一个Tween并开始播放

这里只演示一个最基础的用法

// 基本格式:
let tween = new Tween({ 对象 }).to({ 对象 }, 补间时间).onUpdate((value) => {
    // 可以在这里使用补间对象来进行补间操作
    console.log(value)
})
// 开始播放Tween动画
tween.start();

3.Tween的相关接口

3.1.控制Tween动画

  • start和stop

用来控制一个Tween的开始和停止

tweenA.start(); // 开始播放
tweenA.stop(); // 停止播放
  • chain

制作多个彼此衔接的动画,例如一个动画在另一个动画结束后开始,可以通过 chain 来实现

tweenA.chain(tweenB);  //tweenB 在 tweenA 之后开始动画,故可以制作一个无限循环的动画
tweenB.chain(tweenA);
  • repeat

制作循环动画,接收一个用于描述循环次数的参数

tweenA.repeat(10);
  • delay

用于控制动画之间的延迟

tweenA.delay(1000);
tweenA.start()

3.2.回调函数

  • onStart==>tween动画开始前的回调函数
  • onStop==>tween动画结束后的回调函数
  • onUpdate==>在tween动画每次更新后执行
  • onComplete==>在tween动画全部结束后执行
let tweenA = new TweenUtil.Tween({}).to({}, 1000).
    onStart(() => {
        // 添加逻辑
    }).
    onUpdate(() => {
        // 添加逻辑
    }).
    onStop(() => {
        // 添加逻辑
    }).
    onComplete(() => {
        // 添加逻辑
    })

3.3.缓动函数和缓动方式

  • 缓动函数

Linear ==> 线性匀速运动效果

Quadratic ==> 二次方的缓动(t^2)

Cubic ==> 三次方的缓动()

Quartic ==> 四次方的缓动()

Quintic ==> 五次方的缓动

Sinusoidal ==> 正弦曲线的缓动()

Exponential ==> 指数曲线的缓动()

Circular ==> 圆形曲线的缓动()

Elastic ==> 指数衰减的正弦曲线缓动()

Back ==> 超过范围的三次方的缓动

Bounce ==> 指数衰减的反弹缓动

  • 缓动方式

easeIn(In) ==> 加速,先慢后快

easeOut(Out) ==> 减速,先快后慢

easeInOut(InOut) ==> 前半段加速,后半段减速

使用例子:

new Tween({}).to({}, 1000).onUpdate().easing(TweenUtil.Easing.Cubic.Out)

3.4.插值函数

使用插值函数可以控制Tween的运动曲线,实际应用案例可以参考这篇帖子:感受下无限导弹的魅力吧! - 资源/心得分享 创作者论坛 (ark.online)

testTween() {
    const startLoc = this.gameObject.worldTransform.position;
    // 设置tween的起始位置
    let loc = { x: startLoc.x, y: startLoc.y, z: startLoc.z };
    // 创建新的Tween对象
    const newTween = new Tween(loc);
    console.log("tween created.")

    // 使用插值,to方法里传入的是数组,这里请注意传入的数组是分别针对x,y,z的
    newTween.to({ x: [100, 200, 100, loc.x], y: [100, 200, 100, loc.y], z: [100, 200, 100, loc.z] })
        // 使用interpolation方法传入贝塞尔插值
        .interpolation(TweenUtil.Interpolation.Bezier)
        .onUpdate((val, time) => {
            // 在update中更新代码附加对象的位置
            this.gameObject.worldTransform.position = new Vector(val.x, val.y, val.z);
        })
        .start()
        // 无限重复
        .repeat(Infinity);
}

4.效果演示

4.1.数字补间动画

/**
 * 开启一个数字的补间动画
 * @param oldNum 旧值
 * @param targetNum 目标值
 * @param time 时间(毫秒)
 */
private NumTweenStart(oldNum: number, targetNum: number, time: number) {
    let tween = new Tween({ value: oldNum }).to({ value: targetNum }, time).onUpdate((obj) => {
        // 每一次更新,让控件mNum_txt显示的内容为补间值
        this.mNum_txt.text = obj.value.toFixed(2);
    })
    tween.start();
}

4.2.UI补间动画

/**
 * 开启一个移动UI的补间动画
 * @param targetX 目标位置x
 * @param targetY 目标位置y
 */
private UITweenStart(targetX: number, targetY: number) {
    let targetPosition = new Vector2(targetX, targetY)
    let tween = new Tween(this.mUITween_img.position).to(targetPosition, 1000).onUpdate((obj) => {
        // 每一次更新,改变图片mUITween_img的位置
        this.mUITween_img.position = obj;
    })
    tween.start()
}

4.3.物体补间动画

private async ObjTweenStart(targetX: number, targetY: number, targetZ: number) {
    // 查找物体
    let targetObj = await GameObject.asyncFindGameObjectById("FE9CD8A8")
    // 根据传入值创建目标点坐标
    let targetPosition = new Vector(targetX, targetY, targetZ)
    let tween = new Tween(targetObj.worldTransform.position).to(targetPosition, 1000).onUpdate((obj) => {
        // 每一次更新,改变物体targetObj的位置
        targetObj.worldTransform.position = obj;
    })
    tween.start()
}

UI补间.mp4

12.62 MB, 下载次数: 64

物体补间.mp4

12.64 MB, 下载次数: 55

数字补间.mp4

20.1 MB, 下载次数: 57

回复

使用道具 举报

kk 发表于 2023-4-5 00:01:49 | 显示全部楼层
厉害的
回复

使用道具 举报

今天小雨转甜 发表于 2023-8-3 17:48:04 | 显示全部楼层
请问我想用缓动的一次函数,二次函数组合,实现物体的平抛动画?这种应该怎么处理,原理不太理解。
回复

使用道具 举报

空伊伊楼主 发表于 2023-8-3 17:50:01 | 显示全部楼层
今天小雨转甜 发表于 2023-8-3 17:48
请问我想用缓动的一次函数,二次函数组合,实现物体的平抛动画?这种应该怎么处理,原理不太理解。 ...

看看这个帖子里的代码,我在里面写了对应的代码,应该能满足你的需求,你可以看看https://forum.ark.online/forum.php?mod=viewthread&tid=1483
回复

使用道具 举报

今天小雨转甜 发表于 2023-8-3 18:18:59 | 显示全部楼层
空伊伊 发表于 2023-8-3 17:50
看看这个帖子里的代码,我在里面写了对应的代码,应该能满足你的需求,你可以看看https://forum.ark.onli ...

是不是可以这样理解:在tween里面对路径点的xyz值进行计算和设置,然后用插值函数让整个路径变成平滑的曲线。
因为开始没注意这个插值函数,所以用缓动函数中的 Linear 和 Quadratic 去拼抛物线 ,执行这两个tween对一个物体做位置变化,实现了一个诡异的抛无线。但是对物体的实际位置是怎么计算出来的还没有理解,正在打印位置信息测试中~~。
回复

使用道具 举报

空伊伊楼主 发表于 2023-8-4 11:27:36 | 显示全部楼层
今天小雨转甜 发表于 2023-8-3 18:18
是不是可以这样理解:在tween里面对路径点的xyz值进行计算和设置,然后用插值函数让整个路径变成平滑的曲 ...

对滴
回复

使用道具 举报

你若盛开,蝴蝶自来 发表于 2023-9-6 10:56:29 | 显示全部楼层
本帖最后由 你若盛开,蝴蝶自来 于 2023-9-6 10:58 编辑

开启UI脚本的update要用canUpdate
回复

使用道具 举报

72小时热榜
热门版块
快速回复 返回顶部 返回列表