大坑,同一帧,组件多次update或者可能不会update

一帧mainloop循环中,_compScheduler遍历过程中有comp移除,调用this._zero.fastRemove(comp)移除后,出现遍历下标值递减,造成后续的CCComp执行多次update



这里发的截图,当遍历到i=112时,连续有两个comp移除,第一次移除下标82后,i=111,第二次移除下标80时,i=110。i=111,112这两个comp在同一帧中会调用两次update函数

同样的,末尾被塞到插入位置的comp,就不会有update调用了

一个函数里产生两个严重bug

@panda @huanxinyin

莫名其妙,cocos提供了api,非要自己写

这是引擎源码,可以去看看

这个写法看起来没问题,把需要移除的内容指向末尾,然后让长度减1,这样就移除了i,算法名字也是快速移除。

写的没问题,用起来有问题

那个remove还有个分支,order不等于0,没有使用fastremove。而且如果组件不使用,可以使用enable进行禁用而不是移除。

image
用removeAt同样有问题

image
而且,这里的comp非自定义comp,cc.Label,cc.Button等默认组件都在这里面,节点不用了不可能用enable去禁用。而且源码不管任何形式的操作(Enabled,Disabled)最终都会调到下面有问题的4个函数里

image

知道严重性了吧

看这个代码,ABCD四个组件,B的Update刚执行完,这时移除B,D被放到B的位置,导致D的Update不会执行?

是的,有可能执行F的update移除A,B,C,后面就都乱套了

可以通过数据驱动设计来解决这个问题,确保在同一帧先操作数据,再直接操作实体。
举个栗子:

const flags = [true, false, true, true];

// 修改组件状态
flags[3] = false

// 同步逻辑
update() {
    // 确保所有数据已经修改完毕
    // ...

    // 进行处理
    if (comp.enable && !flags[3]) {
        comp.enable = true
        flags[3] = true
    }
}

这样处理的好处就是,数据在当前帧随便改,改完统一生效。
跟 cocos 的渲染处理机制类似,分为 berforeUpdate 和 afterUpdate。

嗯,修改问题的方式有很多种,你这种就改的比较彻底了。目前只打算把自已确保要每帧update的comp单独拎出来,走额外的分支,确保不会漏掉或者多次执行。

这种问题出现在什么应用场景下。

afk类即时战斗,同一帧多个spine特效移除添加,战斗时间异常,最近各种加日志,追查下来发现是update调用问题(这个问题断断续续查了几个月了 :sweat:

1赞


还有这个问题,也是查了好久才查出来。因为这些底层bug,战斗验证一直有问题,采取了别的临时验证方案

这不就是在遍历循环中添加或者删除一项的经典问题

是的,可能是引擎组考虑update性能问题,这里做了一些优化,反而引起了bug

@dumganhar