缓动示例

本文将主要介绍 Cosos Creator 缓动中常见的一些用法和接口。

构造缓动

通过 tween 方法或使用 new Tween<T>(target: T) 都可以构造缓动。

注意:‘tween’ 是引擎提供的工具方法,并非 ‘Tween’ 的成员,请注意区分。关于这点可以参考接口说明: 缓动接口

链式 API

大部分和动作相关的接口都会返回 this 或者一个新的 Tween 对象,因此可以方便的使用链式调用来进行组合:

  1. tween()
  2. .target(this.node)
  3. .to(1.0, { position: new Vec3(0, 10, 0) })
  4. .by(1.0, { position: new Vec3(0, -10, 0) })
  5. .delay(1.0)
  6. .by(1.0, { position: new Vec3(0, -10, 0) })
  7. .start()

to,by 简单示例

这里演示了如何使用一个 to 类型的缓动绑定节点的位置信息并将其位置沿 Y 轴偏移 10 个单位:

  1. let tweenDuration : number = 1.0; // 缓动的时长
  2. tween(this.node.position)
  3. .to( tweenDuration, new Vec3(0, 10, 0), { // to 接口表示节点的绝对值
  4. onUpdate : (target:Vec3, ratio:number)=>{ // 实现 ITweenOption 的 onUpdate 回调,接受当前缓动的进度
  5. this.node.position = target; // 将缓动系统计算出的结果赋予 node 的位置
  6. }
  7. }).start(); // 调用 start 方法,开启缓动

绑定不同对象

开发中使用 Node 作为绑定目标的情景会更多一些,代码示例如下:

  1. let quat : Quat = new Quat();
  2. Quat.fromEuler(quat, 0, 90, 0);
  3. tween(this.node)
  4. .to(tweenDuration, {
  5. position: new Vec3(0, 10, 0), // 位置缓动
  6. scale: new Vec3(1.2, 3, 1), // 缩放缓动
  7. rotation:quat } // 旋转缓动
  8. )
  9. .start(); // 调用 start 方法,开启缓动

实际上缓动可以绑定任意对象,代码示例如下:

  1. class BindTarget{
  2. color : Color
  3. }
  4. let sprite : Sprite = this.node.getComponent(Sprite) ;
  5. let bindTarget : BindTarget = new BindTarget();
  6. bindTarget.color = Color.BLACK;
  7. tween(bindTarget)
  8. .by( 1.0, { color: Color.RED }, {
  9. onUpdate(tar:BindTarget){
  10. sprite.color = tar.color; // 设置精灵的为 BindTarget 内的颜色
  11. }
  12. })
  13. .start()

常见示例

多个动作

通常来说,一个缓动可由一个或多个 动作 组成,Tween 维护了一个由多个 动作 组成的数据结构用于管理当前缓动内的所有动作。

下面代码演示了将物体的位置沿 Y 轴移动 10 个单位后,沿 -Y 轴移动 10 个单位。

  1. let tweenDuration : number = 1.0;
  2. tween(this.node.position)
  3. .to( tweenDuration, new Vec3(0, 10, 0), {
  4. onUpdate : (target:Vec3, ratio:number)=>{
  5. this.node.position = target;
  6. }
  7. })
  8. .to( tweenDuration, new Vec3(0, -10, 0), {
  9. onUpdate : (target:Vec3, ratio:number)=>{
  10. this.node.position = target;
  11. }
  12. }) // 此时 Tween 内的动作数量为 2

多个缓动也可使用 unionsquenceparallel 接口来组织。通过提前创建好一些固定的缓动,并使用 unionsquenceparallel 来组合他们从而减少代码的编写。

整合多个缓动

union 方法会将当前所有的动作合并为一整个,代码示例如下:

  1. let tweenDuration : number = 1.0;
  2. tween(this.node)
  3. .to(tweenDuration, { position:new Vec3(0, 10, 0) }) // 这里以 node 为缓动的目标
  4. .to(tweenDuration, { position:new Vec3(0, -10, 0) }) // 此时 Tween 内的动作数量为 2
  5. .union() // 这里会将上述的两个缓动整合成一个,此时 Tween 内的动作数量为 1
  6. .start(); // 调用 start 方法,开启缓动

缓动队列

sequence 会将传入的缓动转化为队列形式并加入到当前的缓动内,代码示例如下:

  1. let tweenDuration: number = 1.0;
  2. let t1 = tween(this.node)
  3. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
  4. let t2 = tween(this.node)
  5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
  6. tween(this.node).sequence(t1, t2).start(); // 将 t1 和 t2 两个缓动加入到新的缓动队列内

同时执行多个缓动

parallel 会将传入的缓动转化为并行形式并加入到当前的缓动内,代码示例如下:

  1. let tweenDuration: number = 1.0;
  2. let t1 = tween(this.node)
  3. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
  4. let t2 = tween(this.node)
  5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
  6. tween(this.node).parallel(t1, t2).start(); // 将 t1 和 t2 转化为并行的缓动并加入当前的缓动

插入缓动

then 接口允许传入新的缓动,并将该缓动整合后添加到当前缓动的动作内,代码示例如下:

  1. let tweenAfter = tween(this.node)
  2. .to(1.0, { position: new Vec3(0, -10, 0) })
  3. tween(this.node)
  4. .by(1.0, { position: new Vec3(0, 10, 0) })
  5. .then(tweenAfter)
  6. .start();

延迟执行

delay 会在 当前 的动作 添加一个延时。

注意在下列代码示例中,delay 位置不同会造成完全不同的结果:

  • 延迟 1 秒后,开始进行运动,并连续运动两次。

    1. let tweenDuration: number = 1.0;
    2. tween(this.node)
    3. .delay(1.0)
    4. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
    5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
    6. .start()
  • 在第一次运动后,会延迟 1 秒再做第二次运动。

    1. let tweenDuration: number = 1.0;
    2. tween(this.node)
    3. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
    4. .delay(1.0)
    5. .to(tweenDuration, { position: new Vec3(0, -10, 0) })
    6. .start()

重复执行

接口 repeat 可以为缓动添加一个重复次数,若 embedTween 参数为空,则会使用当前缓动的最后一个动作作为参数。

这意味着,如果当前缓动由多个缓动组成,则只会重复 最后一个,请注意下面的示例:

  1. let tweenDuration: number = 1.0;
  2. tween(this.node)
  3. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
  4. .by(tweenDuration, { position: new Vec3(0, -10, 0) })
  5. .repeat(3) // 注意这里会重复 by 这个缓动 3 次
  6. .start()

若第二个参数 embedTween 不为空,则会重复嵌入的缓动,代码示例如下:

  1. let tweenDuration: number = 1.0;
  2. let embedTween = tween(this.node)
  3. .by(tweenDuration, { position: new Vec3(0, -10, 0) })
  4. tween(this.node)
  5. .to(tweenDuration, { position: new Vec3(0, 10, 0) })
  6. .repeat(3, embedTween) // 这里会重复 embedTween
  7. .start()

repeatForever 接口和 repeat 类似,但是会变为永久重复。

节点相关的缓动

节点相关的方法只适用于 targetNode 的情况。

显示和隐藏节点

showhide 接口可以控制绑定节点的显示和隐藏,下面示例中,节点会被隐藏并在延迟 1 秒后显示。

  1. tween(this.node)
  2. .hide()
  3. .delay(1.0)
  4. .show()
  5. .start();

删除节点

该方法会产生一个 删除节点的动作,该动作会将传入的节点从场景树内删除。

在下面的示例中,节点会在延迟 1 秒后从场景内删除。

  1. tween(this.node)
  2. .delay(1.0)
  3. .removeSelf()
  4. .start()

添加回调动作

call 接口允许给缓动添加一个回调动作,该接口在处理某些异步逻辑时非常有用,示例如下:

  1. tween(this.node)
  2. .to(1.0, { position: new Vec3(0, 10, 0)})
  3. // to 动作完成后会调用该方法
  4. .call( ()=>{
  5. console.log("call");
  6. })
  7. .start()

设置目标属性

通过 set 可以设置目标的属性。下面的示例会在延迟 1 秒后将节点设置在 [0, 100, 0] 的位置。

  1. tween(this.node)
  2. .delay(1.0)
  3. .set({ position: new Vec3(0, 100, 0) })
  4. .start();

也可以同时设置多个不同的属性,代码示例如下:

  1. tween(this.node)
  2. // 同时设置节点的位置,缩放和旋转
  3. .set({ position: new Vec3(0, 100, 0), scale: new Vec3(2, 2, 2), rotation: Quat.IDENTITY } )
  4. .start();

复制缓动

clone 方法可将当前的缓动复制到目标参数上,注意在复制时源缓动和目前缓动的绑定对象要类型一致,即 new Tween<T>(target: T) 中的 T 需要类型一致。代码示例如下:

  1. let srcTween = tween(this.node).delay(1.0).by(1.0, { position: new Vec3(0, 10, 0) })
  2. // 将 ‘srcTween’ 复制到名为 Cone 的节点上
  3. srcTween.clone(find("Cone")).start();

销毁

自动销毁

当缓动目标为 Node 时,将会监听其销毁事件进行缓动的自动销毁,调用 target 方法也会自动更新监听。

手动销毁

大部分缓动在最后一个动作完毕后,都会对自身进行销毁,但是对于未能正确销毁的缓动, 如 repeatForever 在切换场景后,会一直驻留在内存中。需要手动调用销毁接口进行销毁。

如果要停止并销毁缓动,有下列的方法:

  • 成员 stop 接口,销毁该缓动,代码示例如下:

    1. let t = tween(this.node.position)
    2. .to( 1.0, new Vec3(0, 10, 0), {
    3. onUpdate : (target:Vec3, ratio:number)=>{
    4. this.node.position = target;
    5. }
    6. })
    7. t.stop();
  • 使用静态接口 stopAllstopAllByTagstopAllByTarget 销毁所有或特定缓动,代码示例如下:

    1. Tween.stopAll() // 销毁所有缓动
    2. Tween.stopAllByTag(0); // 销毁所有以 0 为标签的缓动
    3. Tween.stopAllByTarget(this.node); // 销毁该节点上的所有缓动

注意:切换场景时记得停止相应的缓动。