参考你的实现,DrawCall降低明显(在背包这种重灾区)
其次性能这块我这里额外添加了字段去优化了一下。大概思路是参考reorderChildren的_dirty字段,额外添加了一个groupDirty字段,在groupZOrder不变动的情况下,避免每帧排序。
这里简单分享一下改动实现:
C++:
TS:
import { UINodeEvent } from "../../common/UI/UINodeEvent";
import { getChildrensInHierarchy } from "../../common/utils/CommonUtils";
const { ccclass, property,menu } = cc._decorator
/**
* UI分层渲染组件,目前只支持Native
*/
@ccclass
@menu("Custom/UIBatchRender")
export default class UIBatchRender extends cc.Component{
protected static __CUSTOM_NAME:string = "UIBatchRender";
protected get __CUSTOM_NAME():string{
return UIBatchRender.__CUSTOM_NAME;
}
private _layoutDirty = true;
private checkEnable():boolean{
if( !(CC_JSB && CC_NATIVERENDERER) ){
return false;
}
if( !(this.node['_proxy'] && this.node['_proxy'].setGlobalZOrder) ){
return false;
}
return true;
}
onEnable(){
if(!this.checkEnable()) return;
this.node.isGroup = true;
this._layoutDirty = true;
cc.director.on(cc.Director.EVENT_AFTER_UPDATE, this.updateLayout, this);
this.onEvents(this.node);
this._addChildrenEventListeners(this.node);
this.updateLayout();
}
onDisable(){
if(!this.checkEnable()) return;
this.node.isGroup = false;
cc.director.off(cc.Director.EVENT_AFTER_UPDATE, this.updateLayout, this);
this.offEvents(this.node);
this._removeChildrenEventListeners(this.node);
this.setGlobalZOrder(this.node,0,true);
}
private onEvents(node:cc.Node){
node.on(UINodeEvent.CHILD_ADDED, this._childAdded, this);
node.on(UINodeEvent.CHILD_REMOVED, this._childRemoved, this);
node.on(UINodeEvent.CHILD_REORDER, this._doLayoutDirty, this);
}
private offEvents(node:cc.Node){
node.off(UINodeEvent.CHILD_ADDED, this._childAdded, this);
node.off(UINodeEvent.CHILD_REMOVED, this._childRemoved, this);
node.off(UINodeEvent.CHILD_REORDER, this._doLayoutDirty, this);
}
private _addChildrenEventListeners(node:cc.Node) {
let children:Array<cc.Node> = [];
getChildrensInHierarchy(node,children)
for (let i = 0; i < children.length; ++i) {
let child = children[i];
this.onEvents(child);
}
}
private _removeChildrenEventListeners(node:cc.Node){
let children:Array<cc.Node> = [];
getChildrensInHierarchy(this.node,children)
for (let i = 0; i < children.length; ++i) {
let child = children[i];
this.offEvents(child)
}
}
private _childAdded(child) {
this.onEvents(child)
this._addChildrenEventListeners(child);
this._doLayoutDirty();
}
private _childRemoved(child) {
this.offEvents(child)
this._removeChildrenEventListeners(child);
this._doLayoutDirty();
}
private _doLayoutDirty(){
this._layoutDirty = true;
}
private setGlobalZOrder(node:cc.Node,loopNum:number = 0,clean:boolean = false){
let childZorder = 1;
if(!clean){
for(let i=0;i<=loopNum;i++){
childZorder *= 10;
}
if(childZorder <= 10){
let loopAddNum = node.children.length.toString().length;
loopNum += loopAddNum;
}else{
loopNum += 1;
}
}
for(let i=0;i<node.children.length;i++){
let child = node.children[i];
if(!clean){
child.globalZOrder = childZorder + i;
}else{
child.globalZOrder = 0;
}
this.setGlobalZOrder(child,loopNum,clean);
}
}
/**
* 更新全局Global字段
* @param force
*/
public updateLayout(force:boolean = false){
if(force || this._layoutDirty){
this._layoutDirty = false;
this.node.groupDirty = true;
this.setGlobalZOrder(this.node);
}
}
}
把上述脚本挂载在需要设置group(UI分批渲染)的节点上。
该脚本会自动跟踪孩子的添加删除等,有更新全局Global字段动作,则会将节点的groupDirty设置为true,触发C++层面的孩子重新排序