从一个奇怪的Tween问题引发的思考

晚上的时候,一个群友问了如下一个问题
“我代码这样写,为啥会执行两次”


神奇,我自己亲自打开3.8.3的Cocos写了一下,确实如此……

并且我拓展了一下,试了一下4个tween一起并行执行,结果如下……

image

第一反应是,6…… :rofl: :rofl: :rofl:

既然这么神奇,那就翻翻源码吧。

从第一个函数parallel出发
image

接着是_wrappedParallel
image

然后是spawn

再然后是spawn的_actionOneTwo
image

然后是spawn的initWithTwoActions

这其中有个小细节,很有趣,在函数initWithDuration中,为了防止产生除0错误,对于持续时长为0秒的Action的持续时间进行了修改
image

很明显,Spawn就是一个对“两Action”进行封装的的壳。

t1和t2被封装成了一个Spawn,然后这个Spawn再跟t3进行封装,然后这个Spawn再跟t4进行封装。。。如此循环下去,直到大家被打成一整个包。但是这个封装的过程中,有趣的地方在于


因为第一个t1和t2打包的Spawn被修改了duration,所以再跟t3封装的时候,d1 > d2,此时t3就会先被打包成一个Sequence,然后再参与封装。同理,t4也先被封装成了一个Sequence,然后参与封装等等等等。

最后,在整个Tween系统的执行中,我们可以在console.log上下个断点,看看执行过程。

第一帧的时候,Tween解包了整个Spawn,大家一起执行,很Happy,输出4个log。
第二帧的时候,Tween发现当前Spawn其实是有持续时间的(就是强制赋予的那个Epsilon),所以整个Action很显然没有isDone()
image
那么整个Spawn就会在第二帧继续执行。

——————————————————————————————————————————
以下内容就是我的猜测了,因为看代码看得头大了。。。

在第二帧的时候,Sequence依次处于执行完毕的状态,所以开始一层一层往上反馈。但是此时t1和t2构成的Spawn已经被立即执行,所以多输出了该Spawn的两条log。然后此时整个Action处于isDone()的状态,ActionManager检测到之后将该Action移除。

——————————————————————————————————————————
以上问题的解决方法其实也很简单,不要创建这种持续时间为0的tween。正常人应该也不会想到这样干的。。。
或者在call之前,增加一个delay的操作。

1赞

有请专业人士 @dumganhar @SmallMain

3.8.3 的确有这个问题。
3.8.5 社区版,做了大量重构,可能顺手修复了。我刚验证了一下,发现社区版没有这个问题,你可以试试。

1赞

补一个单元测试例:

3赞

3.8.5确实修复了该问题 :+1: :+1: :+1:
image