cocos / cocos-engine

Cocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performance, engaging 2D/3D games and instant web entertainment.
https://www.cocos.com/en/creator
Other
8.64k stars 2.01k forks source link

希望能提供一个更高效的 `节点批量排序` 的方法. #17644

Open finscn opened 2 months ago

finscn commented 2 months ago

Use Case

目前 cocos 中 如果要实现 node的排序 要基于 setSiblingIndex() 方法. 这个方法 每次交换 节点 都要 执行很多复杂的操作. 偶尔改变一两个节点的顺序 还能接受. 但是如果要大批量大范围的改动 就很低效率.


    public setSiblingIndex (index: number): void {
        if (!this._parent) {
            return;
        }
        if (this._parent._objFlags & Deactivating) {
            errorID(3821);
            return;
        }
        const siblings = this._parent._children;
        index = index !== -1 ? index : siblings.length - 1;
        const oldIndex = siblings.indexOf(this);
        if (index !== oldIndex) {
            siblings.splice(oldIndex, 1);
            if (index < siblings.length) {
                siblings.splice(index, 0, this);
            } else {
                siblings.push(this);
            }
            this._parent._updateSiblingIndex();
            if (this._onSiblingIndexChanged) {
                this._onSiblingIndexChanged(index);
            }
            this._eventProcessor.onUpdatingSiblingIndex();
        }
    }

改变任何一个node的顺序, 都会执行 splice/push, _updateSiblingIndex, _onSiblingIndexChanged, onUpdatingSiblingIndex.

但是当批量排序时, 通常我们只需要 在排序后, 统一执行一次这些方法.

逻辑可能是这样的:

按照上述逻辑来实现批量排序 性能会更好. 希望官方能够提供类似方法.

Problem Description

如上所述

Proposed Solution

No response

How it works

No response

Alternatives Considered

Additional Information

No response

finscn commented 2 months ago

补充下, 在正常游戏里, 玩家可能要按照不同的逻辑进行排序, 比如按照 y轴 或者更复杂的逻辑. 这部分引擎没法介入, 也不应该介入. 重要的是 引擎已经提供一个 对 children排序后, 需要调用的方法, 供开发者使用.

比如, 给 node 增加一个方法, 让开发者手动调用.

public onSortChildren (): void {
        const children = this.children;
        for (let i = children.length - 1; i >= 0; --i) {
            (children[i] as any)._siblingIndex = i;
        }
         NodeEventProcessor.callbacksInvoker.emit(DispatcherEventType.MARK_LIST_DIRTY);
        this.emit(cc.NodeEventType.CHILDREN_ORDER_CHANGED);
}

顺便问一下, 其实我不太懂这个 NodeEventProcessor.callbacksInvoker.emit(DispatcherEventType.MARK_LIST_DIRTY) 是做什么的. 但是每次改变一个 子节点的 siblingIndex, 内部都会执行这行代码. 这行代码是 因为没有和某个node绑定,理论上也是 批量修改所有children后, 执行一次就行的吧?

whaqzhzd commented 2 months ago

关注,2D游戏这个需求很常见