分享一个Tween动作同步实现方法

  • Creator 版本: 2.4

效果如下:上半部分没有使用同步,下半部分是同步后的效果

扩展如下:以插件脚本的形式对cc.Tween.start进行扩展

let oldStart = cc.Tween.prototype.start;

cc.Tween.prototype.start = function(time) {
    oldStart.call(this);
    //同步时间
    if (time !== undefined && time !== null && this._finalAction instanceof cc.ActionInterval) {
        let action = this._finalAction;
        let t = (cc.sys.now() - time) * 0.001;

        if (this._finalAction instanceof cc.RepeatForever) {
            action = this._finalAction.getInnerAction();
            t %= action.getDuration();
        } else if (action._timesForRepeat > 1) {
            t %= action.getDuration();
        }

        action._elapsed = t;
        action._firstTick = false;
        action.step(0);
    }
    return this;
}

用法:使用的时候传入一个相同的同步时间

        let time = cc.sys.now();
        this.schedule(() => {
            let node = cc.instantiate(this.n);
            node.parent = this.node;
            node.y = - cc.winSize.height * 0.25;
            node.x = 100 * count2++ - cc.winSize.width * 0.5;
            cc.tween(node).
                by(2, { y: 100 }, { easing: "cubicOut" }).
                by(2, { y: -100 }, { easing: "cubicOut" }).
                union().
                repeatForever().
                start(time);
        }, 1, 9, 0.01);

代码报红:使用的时候start会报红,可以修改.d.ts文件

3赞

这种方式有什么优点不?
通常我就监听第一个tween节点的POSITION_CHANGED做同步了

1赞

你这也是一种实现方式,比较依赖事件,如果修改的目标属性没有触发事件是不是就同步不了了。至于你说的优点,从tween的层面实现同步不比事件监听的方式更优雅?

看过去不错哦
更改了一下,应用到 3.4.2 中了,目前只支持重复的动作,instanceof 判定是否是 CCRepeatAction,在 3.4.2 中得再看下官方暴露在哪里。

实际还有一个问题,这个方案需要优化。
进入后台以后,Tween 动作是被暂停的,这时候,时间的计算就是错的了。

确实没有考虑到游戏暂停/进出后台对同步的影响,既然暂停/进出后台会影响到同步时间的计算,一种可行的优化方案是让tween的同步不再依赖时间,而是依赖一个参考tween的进度,代码修改如下:

cc.Tween.prototype.start = function(elapsed) {
    oldStart.call(this);
    //同步时间
    if (elapsed !== undefined && elapsed !== null && this._finalAction instanceof cc.ActionInterval) {
        let action = this._finalAction;
        if (this._finalAction instanceof cc.RepeatForever) {
            action = this._finalAction.getInnerAction();
        }
        action._elapsed = elapsed;
        action._firstTick = false;
        action.step(0);
    }
    return this;
}

/**
 * 获取tween的进度
 * @returns 
 */
cc.Tween.prototype.getElapsed = function() {
    let elapsed = 0;
    if (this._finalAction instanceof cc.ActionInterval) {
        let action = this._finalAction;
        if (this._finalAction instanceof cc.RepeatForever) {
            action = this._finalAction.getInnerAction();
        }
        elapsed = action._elapsed;
    }
    return elapsed;
}

用法:使用一个参考tween的进度来同步

        let referenceTween, tempTween;
        this.schedule(() => {
            let node = cc.instantiate(this.n);
            node.parent = this.node;
            node.y = - cc.winSize.height * 0.25;
            node.x = 100 * count2 - cc.winSize.width * 0.5;

            tempTween = cc.tween(node).
                by(2, { y: 100 }, { easing: "cubicOut" }).
                by(2, { y: -100 }, { easing: "cubicOut" }).
                union().
                repeatForever().
                start(referenceTween ? referenceTween.getElapsed() : null);

            if (count2 === 1) referenceTween = tempTween;

            count2++;
        }, 1, 9, 0.01);

新方案会有 referenceTween 被销毁引起的问题。

你说的销毁是被谁销毁,引擎应该是不会销毁referenceTween,这个应该不会有问题