mirror of
				https://gitee.com/nomat/lcc-ui-sorting-group-demo.git
				synced 2025-11-03 05:55:24 +00:00 
			
		
		
		
	SortingGroup组件可以应用于当前节点或者其子节点上的UI渲染器
This commit is contained in:
		
							
								
								
									
										71
									
								
								assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								assets/lcc-ui-sorting-group/engine-extend/node.jsb.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
import { director, Layers, misc, utils } from "cc";
 | 
			
		||||
import { MeshRenderer } from "cc";
 | 
			
		||||
import { profiler } from "cc";
 | 
			
		||||
import { CCObject } from "cc";
 | 
			
		||||
import { game } from "cc";
 | 
			
		||||
import { Material } from "cc";
 | 
			
		||||
import { Profiler } from "cc";
 | 
			
		||||
import { Node } from "cc";
 | 
			
		||||
import { JSB } from "cc/env";
 | 
			
		||||
 | 
			
		||||
const _constants = {
 | 
			
		||||
    fontSize: 23,
 | 
			
		||||
    quadHeight: 0.4,
 | 
			
		||||
    segmentsPerLine: 8,
 | 
			
		||||
    textureWidth: 256,
 | 
			
		||||
    textureHeight: 256,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if(JSB){
 | 
			
		||||
    //@ts-ignore
 | 
			
		||||
    const Node_ctor = Node.prototype._ctor;
 | 
			
		||||
    //@ts-ignore
 | 
			
		||||
    Node.prototype._ctor = function (name?: string) {
 | 
			
		||||
        Node_ctor.call(this, name);
 | 
			
		||||
        
 | 
			
		||||
        const sharedArrayBuffer = this._getSharedArrayBufferObject();
 | 
			
		||||
        // Uint32Array with 3 elements: eventMask, layer, dirtyFlags
 | 
			
		||||
        this._sharedUint32Arr = new Uint32Array(sharedArrayBuffer, 0, 3);
 | 
			
		||||
        // Int32Array with 1 element: siblingIndex
 | 
			
		||||
        this._sharedInt32Arr = new Int32Array(sharedArrayBuffer, 12, 1);
 | 
			
		||||
        // uiSortingPriority
 | 
			
		||||
        this._sharedFloatArr = new Float32Array(sharedArrayBuffer, 16, 1);
 | 
			
		||||
        // Uint8Array with 3 elements: activeInHierarchy, active, static, uiSortingEnabled
 | 
			
		||||
        this._sharedUint8Arr = new Uint8Array(sharedArrayBuffer, 20, 4);
 | 
			
		||||
 | 
			
		||||
        this._sharedUint32Arr[1] = Layers.Enum.DEFAULT; // this._sharedUint32Arr[1] is layer
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Object.defineProperty(Node.prototype, 'uiSortingEnabled', {
 | 
			
		||||
        configurable: true,
 | 
			
		||||
        enumerable: true,
 | 
			
		||||
        get (): Readonly<Boolean> {
 | 
			
		||||
            return this._sharedUint8Arr[3] != 0; // Uint8, 1: active
 | 
			
		||||
        },
 | 
			
		||||
        set (v) {
 | 
			
		||||
            this._sharedUint8Arr[3] = (v ? 1 : 0); // Uint8, 1: active
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    Object.defineProperty(Node.prototype, 'uiSortingPriority', {
 | 
			
		||||
        configurable: true,
 | 
			
		||||
        enumerable: true,
 | 
			
		||||
        get (): Readonly<number> {
 | 
			
		||||
            return this._sharedFloatArr[0];
 | 
			
		||||
        },
 | 
			
		||||
        set (v) {
 | 
			
		||||
            this._sharedFloatArr[0] = v;
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // 左下角调试节点创建过早,重新创建
 | 
			
		||||
    //@ts-ignore
 | 
			
		||||
    if (profiler._rootNode && profiler._rootNode.isValid){
 | 
			
		||||
        //@ts-ignore
 | 
			
		||||
        profiler._rootNode.destroy();
 | 
			
		||||
        //@ts-ignore
 | 
			
		||||
        profiler._rootNode = null;
 | 
			
		||||
 | 
			
		||||
        profiler.generateNode();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  "ver": "4.0.23",
 | 
			
		||||
  "importer": "typescript",
 | 
			
		||||
  "imported": true,
 | 
			
		||||
  "uuid": "fa9757e8-0812-47bb-b2cd-658d631f676a",
 | 
			
		||||
  "uuid": "29c6a480-4d9e-413e-8a39-0f0f9a59a19c",
 | 
			
		||||
  "files": [],
 | 
			
		||||
  "subMetas": {},
 | 
			
		||||
  "userData": {}
 | 
			
		||||
@@ -1,141 +0,0 @@
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 由于RenderEntity未暴露,所以修改就只有另辟蹊径
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { Graphics } from "cc";
 | 
			
		||||
import { UIRenderer } from "cc";
 | 
			
		||||
import { TiledLayer } from "cc";
 | 
			
		||||
import { UIMeshRenderer } from "cc";
 | 
			
		||||
import { __private,dragonBones,sp } from "cc";
 | 
			
		||||
import { JSB } from "cc/env";
 | 
			
		||||
import { DEFAULT_SORTING_PRIORITY } from "../sorting-define";
 | 
			
		||||
 | 
			
		||||
export enum RenderEntityFloatSharedBufferView {
 | 
			
		||||
    localOpacity,
 | 
			
		||||
    sortingPriority,
 | 
			
		||||
    count,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum RenderEntityUInt8SharedBufferView {
 | 
			
		||||
    colorR,
 | 
			
		||||
    colorG,
 | 
			
		||||
    colorB,
 | 
			
		||||
    colorA,
 | 
			
		||||
    maskMode,
 | 
			
		||||
    count,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum RenderEntityBoolSharedBufferView {
 | 
			
		||||
    colorDirty,
 | 
			
		||||
    enabled,
 | 
			
		||||
    useLocal,
 | 
			
		||||
    count,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RenderEntity类
 | 
			
		||||
 */
 | 
			
		||||
let RenderEntityClass:typeof __private._cocos_2d_renderer_render_entity__RenderEntity = null;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 更新RenderEntity实体
 | 
			
		||||
 */
 | 
			
		||||
export function UpdateRenderEntity(renderEntity:__private._cocos_2d_renderer_render_entity__RenderEntity){
 | 
			
		||||
    if(renderEntity && !RenderEntityClass){
 | 
			
		||||
        RenderEntityClass = renderEntity.constructor as typeof __private._cocos_2d_renderer_render_entity__RenderEntity;
 | 
			
		||||
 | 
			
		||||
        // @ts-ignore
 | 
			
		||||
        RenderEntityClass.prototype.initSharedBuffer = function(){
 | 
			
		||||
            this._sortingPriority = DEFAULT_SORTING_PRIORITY;
 | 
			
		||||
            if (JSB) {
 | 
			
		||||
                //this._sharedBuffer = new Float32Array(RenderEntitySharedBufferView.count);
 | 
			
		||||
                const buffer = this._nativeObj.getEntitySharedBufferForJS();
 | 
			
		||||
                let offset = 0;
 | 
			
		||||
                this._floatSharedBuffer = new Float32Array(buffer, offset, RenderEntityFloatSharedBufferView.count);
 | 
			
		||||
                offset += RenderEntityFloatSharedBufferView.count * 4;
 | 
			
		||||
                this._uint8SharedBuffer = new Uint8Array(buffer, offset, RenderEntityUInt8SharedBufferView.count);
 | 
			
		||||
                offset += RenderEntityUInt8SharedBufferView.count * 1;
 | 
			
		||||
                this._boolSharedBuffer = new Uint8Array(buffer, offset, RenderEntityBoolSharedBufferView.count);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if(!('sortingPriority' in RenderEntityClass.prototype)){
 | 
			
		||||
            Object.defineProperty(RenderEntityClass.prototype, 'sortingPriority', {
 | 
			
		||||
                get: function() { 
 | 
			
		||||
                    return this._sortingPriority; 
 | 
			
		||||
                },
 | 
			
		||||
                set: function(value) { 
 | 
			
		||||
                    this._sortingPriority = value;
 | 
			
		||||
                    // console.log(`JSB sortingPriority ${value}`);
 | 
			
		||||
                    if (JSB) {
 | 
			
		||||
                        this._floatSharedBuffer[RenderEntityFloatSharedBufferView.sortingPriority] = value;
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                enumerable: true
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // @ts-ignore
 | 
			
		||||
        renderEntity.initSharedBuffer();
 | 
			
		||||
 | 
			
		||||
        console.log('Update RenderEntity Class');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
const Graphics_createRenderEntity = Graphics.prototype.createRenderEntity;
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
Graphics.prototype.createRenderEntity = function(){
 | 
			
		||||
    let entity = Graphics_createRenderEntity.call(this);
 | 
			
		||||
    UpdateRenderEntity(entity);
 | 
			
		||||
    return entity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const UIMeshRenderer_onLoad = UIMeshRenderer.prototype.onLoad;
 | 
			
		||||
UIMeshRenderer.prototype.onLoad = function(){
 | 
			
		||||
    UpdateRenderEntity(this._renderEntity);
 | 
			
		||||
    return UIMeshRenderer_onLoad.call(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
const UIRenderer_createRenderEntity = UIRenderer.prototype.createRenderEntity;
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
UIRenderer.prototype.createRenderEntity = function(){
 | 
			
		||||
    let entity = UIRenderer_createRenderEntity.call(this);
 | 
			
		||||
    UpdateRenderEntity(entity);
 | 
			
		||||
    return entity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(dragonBones){
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const ArmatureDisplay_createRenderEntity = dragonBones.ArmatureDisplay.prototype.createRenderEntity;
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    dragonBones.ArmatureDisplay.prototype.createRenderEntity = function(){
 | 
			
		||||
        let entity = ArmatureDisplay_createRenderEntity.call(this);
 | 
			
		||||
        UpdateRenderEntity(entity);
 | 
			
		||||
        return entity;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(sp){
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const Skeleton_createRenderEntity = sp.Skeleton.prototype.createRenderEntity;
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    sp.Skeleton.prototype.createRenderEntity = function(){
 | 
			
		||||
        let entity = Skeleton_createRenderEntity.call(this);
 | 
			
		||||
        UpdateRenderEntity(entity);
 | 
			
		||||
        return entity;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(TiledLayer){
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const TiledLayer_createRenderEntity = TiledLayer.prototype.createRenderEntity;
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    TiledLayer.prototype.createRenderEntity = function(){
 | 
			
		||||
        let entity = TiledLayer_createRenderEntity.call(this);
 | 
			
		||||
        UpdateRenderEntity(entity);
 | 
			
		||||
        return entity;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,31 +4,13 @@ declare module 'cc' {
 | 
			
		||||
    interface UIRenderer {
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序优先级 - private
 | 
			
		||||
         * 渲染优先级
 | 
			
		||||
         */
 | 
			
		||||
        _sortingPriority:number;
 | 
			
		||||
        renderPriority:number;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序优先级
 | 
			
		||||
         * 渲染透明度
 | 
			
		||||
         */
 | 
			
		||||
        sortingPriority:number;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序透明度
 | 
			
		||||
         */
 | 
			
		||||
        sortingOpacity:number;
 | 
			
		||||
        renderOpacity:number;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(!('sortingPriority' in UIRenderer.prototype)){
 | 
			
		||||
    Object.defineProperty(UIRenderer.prototype, 'sortingPriority', {
 | 
			
		||||
        get: function() { 
 | 
			
		||||
            return this._sortingPriority; 
 | 
			
		||||
        },
 | 
			
		||||
        set: function(value) { 
 | 
			
		||||
            this._sortingPriority = value;
 | 
			
		||||
            this._renderEntity.sortingPriority = value;
 | 
			
		||||
        },
 | 
			
		||||
        enumerable: true
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								assets/lcc-ui-sorting-group/engine-extend/ui-transform.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import { UITransform } from "cc";
 | 
			
		||||
import { JSB } from "cc/env";
 | 
			
		||||
 | 
			
		||||
declare module 'cc' {
 | 
			
		||||
    interface UITransform {
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序优先级 - private
 | 
			
		||||
         */
 | 
			
		||||
        _sortingPriority:number;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序优先级
 | 
			
		||||
         */
 | 
			
		||||
        sortingPriority:number;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序优使能 - private
 | 
			
		||||
         */
 | 
			
		||||
        _sortingEnabled:boolean;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 排序优使能
 | 
			
		||||
         */
 | 
			
		||||
        sortingEnabled:boolean;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(!('sortingPriority' in UITransform.prototype)){
 | 
			
		||||
    Object.defineProperty(UITransform.prototype, 'sortingPriority', {
 | 
			
		||||
        get: function() { 
 | 
			
		||||
            return this._sortingPriority; 
 | 
			
		||||
        },
 | 
			
		||||
        set: function(value) { 
 | 
			
		||||
            this._sortingPriority = value;
 | 
			
		||||
            if(JSB){
 | 
			
		||||
                this.node.uiSortingPriority = value;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        enumerable: true
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    Object.defineProperty(UITransform.prototype, 'sortingEnabled', {
 | 
			
		||||
        get: function() { 
 | 
			
		||||
            return this._sortingEnabled; 
 | 
			
		||||
        },
 | 
			
		||||
        set: function(value) { 
 | 
			
		||||
            this._sortingEnabled = value;
 | 
			
		||||
            if(JSB){
 | 
			
		||||
                this.node.uiSortingEnabled = value;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        enumerable: true
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "ver": "4.0.23",
 | 
			
		||||
  "importer": "typescript",
 | 
			
		||||
  "imported": true,
 | 
			
		||||
  "uuid": "421d8e4a-74d5-4851-a739-de22c13f87e3",
 | 
			
		||||
  "files": [],
 | 
			
		||||
  "subMetas": {},
 | 
			
		||||
  "userData": {}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import { clamp, gfx,Node,RenderData,UI,StencilManager,UIRenderer, renderer } from 'cc';
 | 
			
		||||
import { JSB } from 'cc/env';
 | 
			
		||||
import { DEFAULT_SORTING_PRIORITY } from '../sorting-define';
 | 
			
		||||
 | 
			
		||||
declare module 'cc' {
 | 
			
		||||
    interface UI {
 | 
			
		||||
@@ -64,14 +63,14 @@ UI.prototype.flushRendererCache = function(){
 | 
			
		||||
    const rendererCache = this.rendererCache;
 | 
			
		||||
    if(rendererCache.length > 0){
 | 
			
		||||
        if(this.rendererOrder){
 | 
			
		||||
            rendererCache.sort((a, b)=>{ return a._sortingPriority - b._sortingPriority; });
 | 
			
		||||
            rendererCache.sort((a, b)=>{ return a.renderPriority - b.renderPriority; });
 | 
			
		||||
        }
 | 
			
		||||
        // console.log(`flushRendererCache ${rendererCache.length}`);
 | 
			
		||||
        for(let render of rendererCache){
 | 
			
		||||
            // console.log(`${render.node.name} render hash ${render.renderData.dataHash}`);
 | 
			
		||||
            render.fillBuffers(this);
 | 
			
		||||
            if(render.sortingOpacity >= 0){
 | 
			
		||||
                updateOpacity(render.renderData, render.sortingOpacity);
 | 
			
		||||
            if(render.renderOpacity >= 0){
 | 
			
		||||
                updateOpacity(render.renderData, render.renderOpacity);
 | 
			
		||||
                const buffer = render.renderData.getMeshBuffer();
 | 
			
		||||
                if (buffer) {
 | 
			
		||||
                    buffer.setDirty();
 | 
			
		||||
@@ -126,7 +125,7 @@ UI.prototype.update = function() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UI.prototype.walk = function(node: Node, level = 0){
 | 
			
		||||
UI.prototype.walk = function(node: Node, level = 0, sortingPriority = 0){
 | 
			
		||||
    if (!node.activeInHierarchy) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -134,6 +133,8 @@ UI.prototype.walk = function(node: Node, level = 0){
 | 
			
		||||
    const uiProps = node._uiProps;
 | 
			
		||||
    const render = uiProps.uiComp as UIRenderer;
 | 
			
		||||
    const stencilEnterLevel = render && (render.stencilStage === _cocos_2d_renderer_stencil_manager__Stage.ENTER_LEVEL || render.stencilStage === _cocos_2d_renderer_stencil_manager__Stage.ENTER_LEVEL_INVERTED);
 | 
			
		||||
    const transform = uiProps.uiTransformComp;
 | 
			
		||||
    sortingPriority = (transform && transform._sortingEnabled) ? transform._sortingPriority : sortingPriority;
 | 
			
		||||
 | 
			
		||||
    // Save opacity
 | 
			
		||||
    const parentOpacity = this._pOpacity;
 | 
			
		||||
@@ -167,14 +168,14 @@ UI.prototype.walk = function(node: Node, level = 0){
 | 
			
		||||
            }
 | 
			
		||||
        }else{
 | 
			
		||||
            this.rendererCache.push(render);
 | 
			
		||||
            render._sortingPriority = render._sortingPriority ?? DEFAULT_SORTING_PRIORITY;
 | 
			
		||||
            if(render._sortingPriority != DEFAULT_SORTING_PRIORITY){
 | 
			
		||||
            render.renderPriority = sortingPriority;
 | 
			
		||||
            if(sortingPriority != 0){
 | 
			
		||||
                this.rendererOrder = true;
 | 
			
		||||
            }
 | 
			
		||||
            if (this._opacityDirty && render && !render.useVertexOpacity && render.renderData && render.renderData.vertexCount > 0) {
 | 
			
		||||
                render.sortingOpacity = opacity;
 | 
			
		||||
                render.renderOpacity = opacity;
 | 
			
		||||
            }else{
 | 
			
		||||
                render.sortingOpacity = -1;
 | 
			
		||||
                render.renderOpacity = -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -182,7 +183,7 @@ UI.prototype.walk = function(node: Node, level = 0){
 | 
			
		||||
    if (children.length > 0 && !node._static) {
 | 
			
		||||
        for (let i = 0; i < children.length; ++i) {
 | 
			
		||||
            const child = children[i];
 | 
			
		||||
            this.walk(child, level);
 | 
			
		||||
            this.walk(child, level, sortingPriority);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ export enum SortingLayer {
 | 
			
		||||
    //-- 自定义,在此之上,小于 DEFAULT 的层级
 | 
			
		||||
 | 
			
		||||
    /** 
 | 
			
		||||
     * 默认层级,在没有应用排序的UI渲染上的默认层级
 | 
			
		||||
     * 默认层级,在没有应用排序的UI渲染上的默认层级 *不要修改此枚举*
 | 
			
		||||
     */
 | 
			
		||||
    DEFAULT = 0,
 | 
			
		||||
 | 
			
		||||
@@ -21,8 +21,3 @@ export enum SortingLayer {
 | 
			
		||||
 * 在层级中最大排序值
 | 
			
		||||
 */
 | 
			
		||||
export const ORDER_IN_LAYER_MAX = 100000;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 默认排序优先级
 | 
			
		||||
 */
 | 
			
		||||
export const DEFAULT_SORTING_PRIORITY = SortingLayer.DEFAULT * ORDER_IN_LAYER_MAX;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
 | 
			
		||||
import { _decorator, Component, Node, ccenum, CCInteger, CCFloat, Enum, director, UI, UIRenderer } from 'cc';
 | 
			
		||||
import { DEFAULT_SORTING_PRIORITY, ORDER_IN_LAYER_MAX, SortingLayer } from './sorting-define';
 | 
			
		||||
import { _decorator, Component, Node, ccenum, CCInteger, CCFloat, Enum, director, UI, UIRenderer, UITransform } from 'cc';
 | 
			
		||||
import { ORDER_IN_LAYER_MAX, SortingLayer } from './sorting-define';
 | 
			
		||||
const { ccclass, property, type, disallowMultiple, requireComponent, executeInEditMode } = _decorator;
 | 
			
		||||
 | 
			
		||||
@ccclass('lcc-ui/SortingGroup')
 | 
			
		||||
@requireComponent(UIRenderer)
 | 
			
		||||
@requireComponent(UITransform)
 | 
			
		||||
@disallowMultiple(true)
 | 
			
		||||
@executeInEditMode(true)
 | 
			
		||||
export class SortingGroup extends Component {
 | 
			
		||||
@@ -20,20 +20,19 @@ export class SortingGroup extends Component {
 | 
			
		||||
    @property({ type:CCFloat, min: 0, max : ORDER_IN_LAYER_MAX })
 | 
			
		||||
    orderInLayer:number = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * UI渲染器
 | 
			
		||||
     */
 | 
			
		||||
    private _uiRenderer:UIRenderer = null;
 | 
			
		||||
    private _uiTransform:UITransform = null;
 | 
			
		||||
 | 
			
		||||
    onLoad(){
 | 
			
		||||
        this._uiRenderer = this.getComponent(UIRenderer);
 | 
			
		||||
        this._uiTransform = this.getComponent(UITransform);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onEnable(){
 | 
			
		||||
        this._uiRenderer.sortingPriority = Math.sign(this.sortingLayer) * (Math.abs(this.sortingLayer) * ORDER_IN_LAYER_MAX + this.orderInLayer);
 | 
			
		||||
        this._uiTransform.sortingPriority = Math.sign(this.sortingLayer) * (Math.abs(this.sortingLayer) * ORDER_IN_LAYER_MAX + this.orderInLayer);
 | 
			
		||||
        this._uiTransform.sortingEnabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onDisable(){
 | 
			
		||||
        this._uiRenderer.sortingPriority = DEFAULT_SORTING_PRIORITY;
 | 
			
		||||
        this._uiTransform.sortingPriority = 0;
 | 
			
		||||
        this._uiTransform.sortingEnabled = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,11 +27,11 @@
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [],
 | 
			
		||||
    "_prefab": {
 | 
			
		||||
      "__id__": 42
 | 
			
		||||
      "__id__": 57
 | 
			
		||||
    },
 | 
			
		||||
    "autoReleaseAssets": false,
 | 
			
		||||
    "_globals": {
 | 
			
		||||
      "__id__": 43
 | 
			
		||||
      "__id__": 58
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "b977450f-1cd5-49fa-a8db-db655012dcf5"
 | 
			
		||||
  },
 | 
			
		||||
@@ -227,27 +227,33 @@
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 25
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 38
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 50
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 38
 | 
			
		||||
        "__id__": 53
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 39
 | 
			
		||||
        "__id__": 54
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 40
 | 
			
		||||
        "__id__": 55
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 41
 | 
			
		||||
        "__id__": 56
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
    "_lpos": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 959.9999999999999,
 | 
			
		||||
      "x": 960.0000000000001,
 | 
			
		||||
      "y": 540,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
@@ -873,6 +879,9 @@
 | 
			
		||||
    ],
 | 
			
		||||
    "_active": false,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 36
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 37
 | 
			
		||||
      }
 | 
			
		||||
@@ -1024,8 +1033,8 @@
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "sortingLayer": 1,
 | 
			
		||||
    "orderInLayer": 0,
 | 
			
		||||
    "_id": "efCgxc/lVEPqsH8au8/ebz"
 | 
			
		||||
    "orderInLayer": 3,
 | 
			
		||||
    "_id": "b7SyZGN8ZByo48B8plzxZd"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
@@ -1147,9 +1156,6 @@
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 35
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 36
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
@@ -1240,19 +1246,6 @@
 | 
			
		||||
    "_cacheMode": 0,
 | 
			
		||||
    "_id": "27iorVNlhOCpEZnibntFMV"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "5c8c9BaV+VA1Z3pK+Zsi3Cb",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 33
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "sortingLayer": 1,
 | 
			
		||||
    "orderInLayer": 0,
 | 
			
		||||
    "_id": "126CLe+FtLK6NZ5f0v9mHT"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
@@ -1274,6 +1267,529 @@
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "c0+Znh+wNM+4H6zrz3BEP5"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "5c8c9BaV+VA1Z3pK+Zsi3Cb",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 25
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "sortingLayer": 1,
 | 
			
		||||
    "orderInLayer": 2,
 | 
			
		||||
    "_id": "0fSX301tNL3ID+MhrxV/d7"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "Node-001",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 7
 | 
			
		||||
    },
 | 
			
		||||
    "_children": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 39
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 42
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 45
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_active": false,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 48
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 49
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
    "_lpos": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": -218.293,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_lrot": {
 | 
			
		||||
      "__type__": "cc.Quat",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0,
 | 
			
		||||
      "w": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_lscale": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 1,
 | 
			
		||||
      "y": 1,
 | 
			
		||||
      "z": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_layer": 33554432,
 | 
			
		||||
    "_euler": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "5aZiXEa0JHkqYnTDtoDhPZ"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "Label",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 38
 | 
			
		||||
    },
 | 
			
		||||
    "_children": [],
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 40
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 41
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
    "_lpos": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 86.86400000000003,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_lrot": {
 | 
			
		||||
      "__type__": "cc.Quat",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0,
 | 
			
		||||
      "w": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_lscale": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 1,
 | 
			
		||||
      "y": 1,
 | 
			
		||||
      "z": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_layer": 33554432,
 | 
			
		||||
    "_euler": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "fcd9mrQURJl70Yh4NjZFfk"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 39
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_contentSize": {
 | 
			
		||||
      "__type__": "cc.Size",
 | 
			
		||||
      "width": 318.81,
 | 
			
		||||
      "height": 50.4
 | 
			
		||||
    },
 | 
			
		||||
    "_anchorPoint": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0.5,
 | 
			
		||||
      "y": 0.5
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "b4U1x/26ZMYorC7jryRepx"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Label",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 39
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_customMaterial": null,
 | 
			
		||||
    "_srcBlendFactor": 2,
 | 
			
		||||
    "_dstBlendFactor": 4,
 | 
			
		||||
    "_color": {
 | 
			
		||||
      "__type__": "cc.Color",
 | 
			
		||||
      "r": 255,
 | 
			
		||||
      "g": 255,
 | 
			
		||||
      "b": 255,
 | 
			
		||||
      "a": 255
 | 
			
		||||
    },
 | 
			
		||||
    "_string": "label111111111111",
 | 
			
		||||
    "_horizontalAlign": 1,
 | 
			
		||||
    "_verticalAlign": 1,
 | 
			
		||||
    "_actualFontSize": 40,
 | 
			
		||||
    "_fontSize": 40,
 | 
			
		||||
    "_fontFamily": "Arial",
 | 
			
		||||
    "_lineHeight": 40,
 | 
			
		||||
    "_overflow": 0,
 | 
			
		||||
    "_enableWrapText": true,
 | 
			
		||||
    "_font": null,
 | 
			
		||||
    "_isSystemFontUsed": true,
 | 
			
		||||
    "_spacingX": 0,
 | 
			
		||||
    "_isItalic": false,
 | 
			
		||||
    "_isBold": false,
 | 
			
		||||
    "_isUnderline": false,
 | 
			
		||||
    "_underlineHeight": 2,
 | 
			
		||||
    "_cacheMode": 0,
 | 
			
		||||
    "_id": "e78f0Gaa1BVbyP/NEimc2D"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "SpriteSplash",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 38
 | 
			
		||||
    },
 | 
			
		||||
    "_children": [],
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 43
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 44
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
    "_lpos": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 4.493000000000052,
 | 
			
		||||
      "y": 7.488500000000045,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_lrot": {
 | 
			
		||||
      "__type__": "cc.Quat",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0,
 | 
			
		||||
      "w": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_lscale": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 1,
 | 
			
		||||
      "y": 1,
 | 
			
		||||
      "z": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_layer": 33554432,
 | 
			
		||||
    "_euler": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "e9YhmcmeJOWpNBcf0S6ISS"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 42
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_contentSize": {
 | 
			
		||||
      "__type__": "cc.Size",
 | 
			
		||||
      "width": 654.13,
 | 
			
		||||
      "height": 378.563
 | 
			
		||||
    },
 | 
			
		||||
    "_anchorPoint": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0.5,
 | 
			
		||||
      "y": 0.5
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "4cBrueDfBClLeJCqVLHVjF"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Sprite",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 42
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_customMaterial": null,
 | 
			
		||||
    "_srcBlendFactor": 2,
 | 
			
		||||
    "_dstBlendFactor": 4,
 | 
			
		||||
    "_color": {
 | 
			
		||||
      "__type__": "cc.Color",
 | 
			
		||||
      "r": 122,
 | 
			
		||||
      "g": 255,
 | 
			
		||||
      "b": 0,
 | 
			
		||||
      "a": 136
 | 
			
		||||
    },
 | 
			
		||||
    "_spriteFrame": {
 | 
			
		||||
      "__uuid__": "7d8f9b89-4fd1-4c9f-a3ab-38ec7cded7ca@f9941",
 | 
			
		||||
      "__expectedType__": "cc.SpriteFrame"
 | 
			
		||||
    },
 | 
			
		||||
    "_type": 0,
 | 
			
		||||
    "_fillType": 0,
 | 
			
		||||
    "_sizeMode": 0,
 | 
			
		||||
    "_fillCenter": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_fillStart": 0,
 | 
			
		||||
    "_fillRange": 0,
 | 
			
		||||
    "_isTrimmedMode": true,
 | 
			
		||||
    "_useGrayscale": false,
 | 
			
		||||
    "_atlas": null,
 | 
			
		||||
    "_id": "5fDWm0jBRKnJQw/0J3YViz"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "Label-001",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 38
 | 
			
		||||
    },
 | 
			
		||||
    "_children": [],
 | 
			
		||||
    "_active": true,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 46
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 47
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
    "_lpos": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": -47.92500000000001,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_lrot": {
 | 
			
		||||
      "__type__": "cc.Quat",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0,
 | 
			
		||||
      "w": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_lscale": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 1,
 | 
			
		||||
      "y": 1,
 | 
			
		||||
      "z": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_layer": 33554432,
 | 
			
		||||
    "_euler": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "d54BCitp5CbbB0vOFgIGbA"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 45
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_contentSize": {
 | 
			
		||||
      "__type__": "cc.Size",
 | 
			
		||||
      "width": 329.22,
 | 
			
		||||
      "height": 50.4
 | 
			
		||||
    },
 | 
			
		||||
    "_anchorPoint": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0.5,
 | 
			
		||||
      "y": 0.5
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "77RtH07pZMGoZCo4eGSEh+"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Label",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 45
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_customMaterial": null,
 | 
			
		||||
    "_srcBlendFactor": 2,
 | 
			
		||||
    "_dstBlendFactor": 4,
 | 
			
		||||
    "_color": {
 | 
			
		||||
      "__type__": "cc.Color",
 | 
			
		||||
      "r": 255,
 | 
			
		||||
      "g": 255,
 | 
			
		||||
      "b": 255,
 | 
			
		||||
      "a": 255
 | 
			
		||||
    },
 | 
			
		||||
    "_string": "label22222222222",
 | 
			
		||||
    "_horizontalAlign": 1,
 | 
			
		||||
    "_verticalAlign": 1,
 | 
			
		||||
    "_actualFontSize": 40,
 | 
			
		||||
    "_fontSize": 40,
 | 
			
		||||
    "_fontFamily": "Arial",
 | 
			
		||||
    "_lineHeight": 40,
 | 
			
		||||
    "_overflow": 0,
 | 
			
		||||
    "_enableWrapText": true,
 | 
			
		||||
    "_font": null,
 | 
			
		||||
    "_isSystemFontUsed": true,
 | 
			
		||||
    "_spacingX": 0,
 | 
			
		||||
    "_isItalic": false,
 | 
			
		||||
    "_isBold": false,
 | 
			
		||||
    "_isUnderline": false,
 | 
			
		||||
    "_underlineHeight": 2,
 | 
			
		||||
    "_cacheMode": 0,
 | 
			
		||||
    "_id": "b295f53fRNOoD+5khP4lUS"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 38
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_contentSize": {
 | 
			
		||||
      "__type__": "cc.Size",
 | 
			
		||||
      "width": 100,
 | 
			
		||||
      "height": 100
 | 
			
		||||
    },
 | 
			
		||||
    "_anchorPoint": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0.5,
 | 
			
		||||
      "y": 0.5
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "f3nDcnVYdN6pZUfDtW0fDt"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "5c8c9BaV+VA1Z3pK+Zsi3Cb",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 38
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "sortingLayer": 1,
 | 
			
		||||
    "orderInLayer": 1,
 | 
			
		||||
    "_id": "e8pXnnkRRPfo50O0CR7SRu"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Node",
 | 
			
		||||
    "_name": "SpriteSplash",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "_parent": {
 | 
			
		||||
      "__id__": 7
 | 
			
		||||
    },
 | 
			
		||||
    "_children": [],
 | 
			
		||||
    "_active": false,
 | 
			
		||||
    "_components": [
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 51
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "__id__": 52
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "_prefab": null,
 | 
			
		||||
    "_lpos": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": -630.484,
 | 
			
		||||
      "y": -352.824,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_lrot": {
 | 
			
		||||
      "__type__": "cc.Quat",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0,
 | 
			
		||||
      "w": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_lscale": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 1,
 | 
			
		||||
      "y": 1,
 | 
			
		||||
      "z": 1
 | 
			
		||||
    },
 | 
			
		||||
    "_layer": 33554432,
 | 
			
		||||
    "_euler": {
 | 
			
		||||
      "__type__": "cc.Vec3",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0,
 | 
			
		||||
      "z": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "c0VfwcFp5DzZIfeDWG4MA7"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 50
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_contentSize": {
 | 
			
		||||
      "__type__": "cc.Size",
 | 
			
		||||
      "width": 654.13,
 | 
			
		||||
      "height": 378.563
 | 
			
		||||
    },
 | 
			
		||||
    "_anchorPoint": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0.5,
 | 
			
		||||
      "y": 0.5
 | 
			
		||||
    },
 | 
			
		||||
    "_id": "16eXrC15RCR7XXix5wahAm"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.Sprite",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
    "_objFlags": 0,
 | 
			
		||||
    "node": {
 | 
			
		||||
      "__id__": 50
 | 
			
		||||
    },
 | 
			
		||||
    "_enabled": true,
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_customMaterial": null,
 | 
			
		||||
    "_srcBlendFactor": 2,
 | 
			
		||||
    "_dstBlendFactor": 4,
 | 
			
		||||
    "_color": {
 | 
			
		||||
      "__type__": "cc.Color",
 | 
			
		||||
      "r": 255,
 | 
			
		||||
      "g": 0,
 | 
			
		||||
      "b": 0,
 | 
			
		||||
      "a": 136
 | 
			
		||||
    },
 | 
			
		||||
    "_spriteFrame": {
 | 
			
		||||
      "__uuid__": "7d8f9b89-4fd1-4c9f-a3ab-38ec7cded7ca@f9941",
 | 
			
		||||
      "__expectedType__": "cc.SpriteFrame"
 | 
			
		||||
    },
 | 
			
		||||
    "_type": 0,
 | 
			
		||||
    "_fillType": 0,
 | 
			
		||||
    "_sizeMode": 0,
 | 
			
		||||
    "_fillCenter": {
 | 
			
		||||
      "__type__": "cc.Vec2",
 | 
			
		||||
      "x": 0,
 | 
			
		||||
      "y": 0
 | 
			
		||||
    },
 | 
			
		||||
    "_fillStart": 0,
 | 
			
		||||
    "_fillRange": 0,
 | 
			
		||||
    "_isTrimmedMode": true,
 | 
			
		||||
    "_useGrayscale": false,
 | 
			
		||||
    "_atlas": null,
 | 
			
		||||
    "_id": "b9Wwwx4phE7oA1FN/IyNcP"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.UITransform",
 | 
			
		||||
    "_name": "",
 | 
			
		||||
@@ -1321,8 +1837,8 @@
 | 
			
		||||
    "__prefab": null,
 | 
			
		||||
    "_alignFlags": 45,
 | 
			
		||||
    "_target": null,
 | 
			
		||||
    "_left": -1.1368683772161603e-13,
 | 
			
		||||
    "_right": -1.1368683772161603e-13,
 | 
			
		||||
    "_left": 1.1368683772161603e-13,
 | 
			
		||||
    "_right": 1.1368683772161603e-13,
 | 
			
		||||
    "_top": 0,
 | 
			
		||||
    "_bottom": 0,
 | 
			
		||||
    "_horizontalCenter": 0,
 | 
			
		||||
@@ -1379,19 +1895,19 @@
 | 
			
		||||
  {
 | 
			
		||||
    "__type__": "cc.SceneGlobals",
 | 
			
		||||
    "ambient": {
 | 
			
		||||
      "__id__": 44
 | 
			
		||||
      "__id__": 59
 | 
			
		||||
    },
 | 
			
		||||
    "shadows": {
 | 
			
		||||
      "__id__": 45
 | 
			
		||||
      "__id__": 60
 | 
			
		||||
    },
 | 
			
		||||
    "_skybox": {
 | 
			
		||||
      "__id__": 46
 | 
			
		||||
      "__id__": 61
 | 
			
		||||
    },
 | 
			
		||||
    "fog": {
 | 
			
		||||
      "__id__": 47
 | 
			
		||||
      "__id__": 62
 | 
			
		||||
    },
 | 
			
		||||
    "octree": {
 | 
			
		||||
      "__id__": 48
 | 
			
		||||
      "__id__": 63
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,10 +10,27 @@ if(NOT DEFINED COCOS_SOURCE_PLUGIN_INCLUDES)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# 排除替换源文件
 | 
			
		||||
list(APPEND COCOS_SOURCE_PLUGIN_EXCULDE "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.h" "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.cpp" "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.h" "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.cpp")
 | 
			
		||||
list(APPEND COCOS_SOURCE_PLUGIN_EXCULDE 
 | 
			
		||||
    "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.h" 
 | 
			
		||||
    "${COCOS_X_PATH}/cocos/2d/renderer/Batcher2d.cpp" 
 | 
			
		||||
    "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.h" 
 | 
			
		||||
    "${COCOS_X_PATH}/cocos/2d/renderer/RenderEntity.cpp"
 | 
			
		||||
    "${COCOS_X_PATH}/cocos/core/scene-graph/Node.h" 
 | 
			
		||||
    "${COCOS_X_PATH}/cocos/core/scene-graph/Node.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# 添加替换源文件
 | 
			
		||||
list(APPEND COCOS_SOURCE_PLUGIN_LIST "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.h" "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.cpp" "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.h" "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.cpp")
 | 
			
		||||
list(APPEND COCOS_SOURCE_PLUGIN_LIST 
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.h" 
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/Batcher2d.cpp"
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.h" 
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos/2d/renderer/RenderEntity.cpp"
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos/core/scene-graph/Node.h" 
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos/core/scene-graph/Node.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# 添加替换源文件
 | 
			
		||||
list(APPEND COCOS_SOURCE_PLUGIN_INCLUDES "${CMAKE_CURRENT_LIST_DIR}/native" "${CMAKE_CURRENT_LIST_DIR}/native/cocos")
 | 
			
		||||
list(APPEND COCOS_SOURCE_PLUGIN_INCLUDES 
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native" 
 | 
			
		||||
    "${CMAKE_CURRENT_LIST_DIR}/native/cocos"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -102,17 +102,19 @@ void Batcher2d::syncRootNodesToNative(ccstd::vector<Node*>&& rootNodes) {
 | 
			
		||||
 | 
			
		||||
void Batcher2d::fillBuffersAndMergeBatches() {
 | 
			
		||||
    for (auto* rootNode : _rootNodeArr) {
 | 
			
		||||
        walk(rootNode, 1);
 | 
			
		||||
        walk(rootNode, 1, true, 0);
 | 
			
		||||
        // CC_LOG_INFO("-------------- flushRendererCache 1 -------------- %d", _rootNodeArr.size());
 | 
			
		||||
        flushRendererCache(); // LCC_UI_SORTING_GROUP
 | 
			
		||||
        generateBatch(_currEntity, _currDrawInfo);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recursion)
 | 
			
		||||
void Batcher2d::walk(Node* node, float parentOpacity, bool cacheEnable, float sortingPriority) { // NOLINT(misc-no-recursion)
 | 
			
		||||
    if (!node->isActiveInHierarchy()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    sortingPriority = node->isUISortingEnabled() ? node->getUISortingPriority() : sortingPriority;
 | 
			
		||||
 | 
			
		||||
    bool breakWalk = false;
 | 
			
		||||
    auto* entity = static_cast<RenderEntity*>(node->getUserData());
 | 
			
		||||
    if (entity) {
 | 
			
		||||
@@ -126,7 +128,7 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs
 | 
			
		||||
 | 
			
		||||
        // LCC_UI_SORTING_GROUP
 | 
			
		||||
        if (entity->isEnabled()) {
 | 
			
		||||
            if(entity->getIsMask()){
 | 
			
		||||
            if(entity->getIsMask() || !cacheEnable){
 | 
			
		||||
                // CC_LOG_INFO("-------------- flushRendererCache 2 --------------");
 | 
			
		||||
                flushRendererCache(); 
 | 
			
		||||
                uint32_t size = entity->getRenderDrawInfosSize();
 | 
			
		||||
@@ -137,7 +139,8 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs
 | 
			
		||||
                entity->setVBColorDirty(false);
 | 
			
		||||
            }else{
 | 
			
		||||
                rendererCache.push_back(entity);
 | 
			
		||||
                if(entity->getSortingPriority() != 0){
 | 
			
		||||
                entity->setRenderPriority(sortingPriority);
 | 
			
		||||
                if(sortingPriority != 0){
 | 
			
		||||
                    rendererOrder = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -153,7 +156,7 @@ void Batcher2d::walk(Node* node, float parentOpacity) { // NOLINT(misc-no-recurs
 | 
			
		||||
        float thisOpacity = entity ? entity->getOpacity() : parentOpacity;
 | 
			
		||||
        for (const auto& child : children) {
 | 
			
		||||
            // we should find parent opacity recursively upwards if it doesn't have an entity.
 | 
			
		||||
            walk(child, thisOpacity);
 | 
			
		||||
            walk(child, thisOpacity, cacheEnable, sortingPriority);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -311,7 +314,7 @@ CC_FORCE_INLINE void Batcher2d::handleMiddlewareDraw(RenderEntity* entity, Rende
 | 
			
		||||
 | 
			
		||||
CC_FORCE_INLINE void Batcher2d::handleSubNode(RenderEntity* entity, RenderDrawInfo* drawInfo) { // NOLINT
 | 
			
		||||
    if (drawInfo->getSubNode()) {
 | 
			
		||||
        walk(drawInfo->getSubNode(), entity->getOpacity());
 | 
			
		||||
        walk(drawInfo->getSubNode(), entity->getOpacity(), false, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -635,13 +638,13 @@ void Batcher2d::createClearModel() {
 | 
			
		||||
void Batcher2d::flushRendererCache() {
 | 
			
		||||
	if(rendererCache.size() > 0){
 | 
			
		||||
        if(rendererOrder){
 | 
			
		||||
            std::stable_sort(rendererCache.begin(), rendererCache.end(), [](RenderEntity* a, RenderEntity* b) { return a->getSortingPriority() < b->getSortingPriority(); });
 | 
			
		||||
            std::stable_sort(rendererCache.begin(), rendererCache.end(), [](RenderEntity* a, RenderEntity* b) { return a->getRenderPriority() < b->getRenderPriority(); });
 | 
			
		||||
        }
 | 
			
		||||
        // CC_LOG_INFO("flushRendererCache %d", rendererCache.size());
 | 
			
		||||
        for(ccstd::vector<RenderEntity*>::iterator it = rendererCache.begin(); it != rendererCache.end(); it++)
 | 
			
		||||
        {
 | 
			
		||||
            RenderEntity* entity = *it;
 | 
			
		||||
            // CC_LOG_INFO("%f", entity->getSortingPriority());
 | 
			
		||||
            // CC_LOG_INFO("%f", entity->getRenderPriority());
 | 
			
		||||
            uint32_t size = entity->getRenderDrawInfosSize();
 | 
			
		||||
            for (uint32_t i = 0; i < size; i++) {
 | 
			
		||||
                auto* drawInfo = entity->getRenderDrawInfoAt(i);
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ public:
 | 
			
		||||
    void updateDescriptorSet();
 | 
			
		||||
 | 
			
		||||
    void fillBuffersAndMergeBatches();
 | 
			
		||||
    void walk(Node* node, float parentOpacity);
 | 
			
		||||
    void walk(Node* node, float parentOpacity, bool cacheEnable, float sortingPriority);
 | 
			
		||||
    void handlePostRender(RenderEntity* entity);
 | 
			
		||||
    void handleDrawInfo(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node);
 | 
			
		||||
    void handleComponentDraw(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node);
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,6 @@ enum class MaskMode : uint8_t {
 | 
			
		||||
 | 
			
		||||
struct EntityAttrLayout {
 | 
			
		||||
    float localOpacity{1.0F};
 | 
			
		||||
    float sortingPriority{0.0F}; // LCC_UI_SORTING_GROUP
 | 
			
		||||
    uint8_t colorR{255};
 | 
			
		||||
    uint8_t colorG{255};
 | 
			
		||||
    uint8_t colorB{255};
 | 
			
		||||
@@ -126,7 +125,6 @@ public:
 | 
			
		||||
    inline Color getColor() const { return Color(_entityAttrLayout.colorR, _entityAttrLayout.colorG, _entityAttrLayout.colorB, _entityAttrLayout.colorA); }
 | 
			
		||||
    inline float getColorAlpha() const { return static_cast<float>(_entityAttrLayout.colorA) / 255.F; }
 | 
			
		||||
    inline float getLocalOpacity() const { return _entityAttrLayout.localOpacity; }
 | 
			
		||||
    inline float getSortingPriority() const { return _entityAttrLayout.sortingPriority; } // LCC_UI_SORTING_GROUP
 | 
			
		||||
    inline float getOpacity() const { return _opacity; }
 | 
			
		||||
    inline void setOpacity(float opacity) { _opacity = opacity; }
 | 
			
		||||
    inline bool isEnabled() const { return _entityAttrLayout.enabledIndex != 0; }
 | 
			
		||||
@@ -137,6 +135,9 @@ public:
 | 
			
		||||
        return _renderEntityType == RenderEntityType::STATIC ? &(_staticDrawInfos[index]) : _dynamicDrawInfos[index];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline float getRenderPriority() const { return _renderPriority; }
 | 
			
		||||
    inline void setRenderPriority(float value) { _renderPriority = value; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    CC_DISALLOW_COPY_MOVE_ASSIGN(RenderEntity);
 | 
			
		||||
    // weak reference
 | 
			
		||||
@@ -157,5 +158,7 @@ private:
 | 
			
		||||
    RenderEntityType _renderEntityType{RenderEntityType::STATIC};
 | 
			
		||||
    uint8_t _staticDrawInfoSize{0};
 | 
			
		||||
    bool _vbColorDirty{true};
 | 
			
		||||
 | 
			
		||||
    float _renderPriority{0.0F};
 | 
			
		||||
};
 | 
			
		||||
} // namespace cc
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,984 @@
 | 
			
		||||
/****************************************************************************
 | 
			
		||||
 Copyright (c) 2021 Xiamen Yaji Software Co., Ltd.
 | 
			
		||||
 | 
			
		||||
 http://www.cocos.com
 | 
			
		||||
 | 
			
		||||
 Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 of this software and associated engine source code (the "Software"), a limited,
 | 
			
		||||
 worldwide, royalty-free, non-assignable, revocable and non-exclusive license
 | 
			
		||||
 to use Cocos Creator solely to develop games on your target platforms. You shall
 | 
			
		||||
 not use Cocos Creator software for developing other software or tools that's
 | 
			
		||||
 used for developing games. You are not granted to publish, distribute,
 | 
			
		||||
 sublicense, and/or sell copies of Cocos Creator.
 | 
			
		||||
 | 
			
		||||
 The software or tools in this License Agreement are licensed, not sold.
 | 
			
		||||
 Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
 | 
			
		||||
 | 
			
		||||
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 THE SOFTWARE.
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "core/scene-graph/Node.h"
 | 
			
		||||
#include "base/StringUtil.h"
 | 
			
		||||
#include "core/data/Object.h"
 | 
			
		||||
#include "core/memop/CachedArray.h"
 | 
			
		||||
#include "core/platform/Debug.h"
 | 
			
		||||
#include "core/scene-graph/NodeEnum.h"
 | 
			
		||||
#include "core/scene-graph/Scene.h"
 | 
			
		||||
#include "core/utils/IDGenerator.h"
 | 
			
		||||
#include "math/Utils.h"
 | 
			
		||||
 | 
			
		||||
namespace cc {
 | 
			
		||||
 | 
			
		||||
// static variables
 | 
			
		||||
 | 
			
		||||
uint32_t Node::clearFrame{0};
 | 
			
		||||
uint32_t Node::clearRound{1000};
 | 
			
		||||
const uint32_t Node::TRANSFORM_ON{1 << 0};
 | 
			
		||||
uint32_t Node::globalFlagChangeVersion{0};
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
const ccstd::string EMPTY_NODE_NAME;
 | 
			
		||||
IDGenerator idGenerator("Node");
 | 
			
		||||
 | 
			
		||||
ccstd::vector<Node *> dirtyNodes;
 | 
			
		||||
CC_FORCE_INLINE void setDirtyNode(const index_t idx, Node *node) {
 | 
			
		||||
    if (idx >= dirtyNodes.size()) {
 | 
			
		||||
        if (idx >= dirtyNodes.capacity()) {
 | 
			
		||||
            size_t minCapacity = std::max((idx + 1) * 2, 32);
 | 
			
		||||
            if (minCapacity > dirtyNodes.capacity()) {
 | 
			
		||||
                dirtyNodes.reserve(minCapacity); // Make a pre-allocated size for dirtyNode vector for better grow performance.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        dirtyNodes.resize(idx + 1, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
    dirtyNodes[idx] = node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CC_FORCE_INLINE Node *getDirtyNode(const index_t idx) {
 | 
			
		||||
    if (idx < 0 || idx >= dirtyNodes.size()) {
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    return dirtyNodes[idx];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
Node::Node() : Node(EMPTY_NODE_NAME) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Node::Node(const ccstd::string &name) {
 | 
			
		||||
#define NODE_SHARED_MEMORY_BYTE_LENGTH (24)
 | 
			
		||||
    static_assert(offsetof(Node, _uiSortingEnabled) + sizeof(_uiSortingEnabled) - offsetof(Node, _eventMask) == NODE_SHARED_MEMORY_BYTE_LENGTH, "Wrong shared memory size");
 | 
			
		||||
    _sharedMemoryActor.initialize(&_eventMask, NODE_SHARED_MEMORY_BYTE_LENGTH);
 | 
			
		||||
#undef NODE_SHARED_MEMORY_BYTE_LENGTH
 | 
			
		||||
 | 
			
		||||
    _id = idGenerator.getNewId();
 | 
			
		||||
    if (name.empty()) {
 | 
			
		||||
        _name.append("New Node");
 | 
			
		||||
    } else {
 | 
			
		||||
        _name = name;
 | 
			
		||||
    }
 | 
			
		||||
    _eventProcessor = ccnew NodeEventProcessor(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Node::~Node() {
 | 
			
		||||
    CC_SAFE_DELETE(_eventProcessor);
 | 
			
		||||
    if (!_children.empty()) {
 | 
			
		||||
        // Reset children's _parent to nullptr to avoid dangerous pointer
 | 
			
		||||
        for (const auto &child : _children) {
 | 
			
		||||
            child->_parent = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::onBatchCreated(bool dontChildPrefab) {
 | 
			
		||||
    // onBatchCreated was implemented in TS, so code should never go here.
 | 
			
		||||
    CC_ASSERT(false);
 | 
			
		||||
    emit(EventTypesToJS::NODE_ON_BATCH_CREATED, dontChildPrefab);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Node *Node::instantiate(Node *cloned, bool isSyncedNode) {
 | 
			
		||||
    if (!cloned) {
 | 
			
		||||
        CC_ASSERT(false);
 | 
			
		||||
        // TODO(): cloned = legacyCC.instantiate._clone(this, this);
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    // TODO():
 | 
			
		||||
    // const newPrefabInfo = cloned._prefab;
 | 
			
		||||
    // if (EDITOR && newPrefabInfo) {
 | 
			
		||||
    //    if (cloned == = newPrefabInfo.root) {
 | 
			
		||||
    //        // newPrefabInfo.fileId = '';
 | 
			
		||||
    //    } else {
 | 
			
		||||
    //        // var PrefabUtils = Editor.require('scene://utils/prefab');
 | 
			
		||||
    //        // PrefabUtils.unlinkPrefab(cloned);
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
    // if (EDITOR && legacyCC.GAME_VIEW) {
 | 
			
		||||
    //    const syncing = newPrefabInfo&& cloned == = newPrefabInfo.root && newPrefabInfo.sync;
 | 
			
		||||
    //    if (!syncing) {
 | 
			
		||||
    //        cloned._name += ' (Clone)';
 | 
			
		||||
    //    }
 | 
			
		||||
    //}
 | 
			
		||||
    cloned->_parent = nullptr;
 | 
			
		||||
    cloned->onBatchCreated(isSyncedNode);
 | 
			
		||||
    return cloned;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::onHierarchyChangedBase(Node *oldParent) { // NOLINT(misc-unused-parameters)
 | 
			
		||||
    Node *newParent = _parent;
 | 
			
		||||
    auto *scene = dynamic_cast<Scene *>(newParent);
 | 
			
		||||
    if (isPersistNode() && scene == nullptr) {
 | 
			
		||||
        emit(EventTypesToJS::NODE_REMOVE_PERSIST_ROOT_NODE);
 | 
			
		||||
#if CC_EDITOR
 | 
			
		||||
        debug::warnID(1623);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
#if CC_EDITOR
 | 
			
		||||
    auto *curScene = getScene();
 | 
			
		||||
    const bool inCurrentSceneBefore = oldParent && oldParent->isChildOf(curScene);
 | 
			
		||||
    const bool inCurrentSceneNow = newParent && newParent->isChildOf(curScene);
 | 
			
		||||
    if (!inCurrentSceneBefore && inCurrentSceneNow) {
 | 
			
		||||
        // attached
 | 
			
		||||
        this->notifyEditorAttached(true);
 | 
			
		||||
    } else if (inCurrentSceneBefore && !inCurrentSceneNow) {
 | 
			
		||||
        // detached
 | 
			
		||||
        this->notifyEditorAttached(false);
 | 
			
		||||
    }
 | 
			
		||||
    // conflict detection
 | 
			
		||||
    // _Scene.DetectConflict.afterAddChild(this);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    bool shouldActiveNow = isActive() && !!(newParent && newParent->isActiveInHierarchy());
 | 
			
		||||
    if (isActiveInHierarchy() != shouldActiveNow) {
 | 
			
		||||
        // Director::getInstance()->getNodeActivator()->activateNode(this, shouldActiveNow); // TODO(xwx): use TS temporarily
 | 
			
		||||
        emit(EventTypesToJS::NODE_ACTIVE_NODE, shouldActiveNow);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::off(const CallbacksInvoker::KeyType &type, bool useCapture) {
 | 
			
		||||
    _eventProcessor->offAll(type, useCapture);
 | 
			
		||||
    bool hasListeners = _eventProcessor->hasEventListener(type);
 | 
			
		||||
    if (!hasListeners) {
 | 
			
		||||
        if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
            _eventMask &= ~TRANSFORM_ON;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::off(const CallbacksInvoker::KeyType &type, const CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    _eventProcessor->off(type, cbID, useCapture);
 | 
			
		||||
    bool hasListeners = _eventProcessor->hasEventListener(type);
 | 
			
		||||
    if (!hasListeners) {
 | 
			
		||||
        if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
            _eventMask &= ~TRANSFORM_ON;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::off(const CallbacksInvoker::KeyType &type, void *target, bool useCapture) {
 | 
			
		||||
    _eventProcessor->off(type, target, useCapture);
 | 
			
		||||
    bool hasListeners = _eventProcessor->hasEventListener(type);
 | 
			
		||||
    if (!hasListeners) {
 | 
			
		||||
        if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
            _eventMask &= ~TRANSFORM_ON;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//void Node::dispatchEvent(event::Event *eve) {
 | 
			
		||||
//    _eventProcessor->dispatchEvent(eve);
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
bool Node::hasEventListener(const CallbacksInvoker::KeyType &type) const {
 | 
			
		||||
    return _eventProcessor->hasEventListener(type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, const CallbackID &cbID) const {
 | 
			
		||||
    return _eventProcessor->hasEventListener(type, cbID);
 | 
			
		||||
}
 | 
			
		||||
bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, void *target) const {
 | 
			
		||||
    return _eventProcessor->hasEventListener(type, target);
 | 
			
		||||
}
 | 
			
		||||
bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, void *target, const CallbackID &cbID) const {
 | 
			
		||||
    return _eventProcessor->hasEventListener(type, target, cbID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::targetOff(const CallbacksInvoker::KeyType &type) {
 | 
			
		||||
    _eventProcessor->targetOff(type);
 | 
			
		||||
    if ((_eventMask & TRANSFORM_ON) && !_eventProcessor->hasEventListener(NodeEventType::TRANSFORM_CHANGED)) {
 | 
			
		||||
        _eventMask &= ~TRANSFORM_ON;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setActive(bool isActive) {
 | 
			
		||||
    uint8_t isActiveU8 = isActive ? 1 : 0;
 | 
			
		||||
    if (_active != isActiveU8) {
 | 
			
		||||
        _active = isActiveU8;
 | 
			
		||||
        Node *parent = _parent;
 | 
			
		||||
        if (parent) {
 | 
			
		||||
            bool couldActiveInScene = parent->isActiveInHierarchy();
 | 
			
		||||
            if (couldActiveInScene) {
 | 
			
		||||
                // Director::getInstance()->getNodeActivator()->activateNode(this, isActive); // TODO(xwx): use TS temporarily
 | 
			
		||||
                emit(EventTypesToJS::NODE_ACTIVE_NODE, isActive);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setParent(Node *parent, bool isKeepWorld /* = false */) {
 | 
			
		||||
    if (isKeepWorld) {
 | 
			
		||||
        updateWorldTransform();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (_parent == parent) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Node *oldParent = _parent;
 | 
			
		||||
    Node *newParent = parent;
 | 
			
		||||
#if CC_DEBUG > 0
 | 
			
		||||
    if (oldParent && (oldParent->_objFlags & Flags::DEACTIVATING) == Flags::DEACTIVATING) {
 | 
			
		||||
        debug::errorID(3821);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    _parent = newParent;
 | 
			
		||||
    _siblingIndex = 0;
 | 
			
		||||
    onSetParent(oldParent, isKeepWorld);
 | 
			
		||||
    emit(NodeEventType::PARENT_CHANGED, oldParent);
 | 
			
		||||
    if (oldParent) {
 | 
			
		||||
        if (!(oldParent->_objFlags & Flags::DESTROYING)) {
 | 
			
		||||
            index_t removeAt = getIdxOfChild(oldParent->_children, this);
 | 
			
		||||
            // TODO(): DEV
 | 
			
		||||
            /*if (DEV && removeAt < 0) {
 | 
			
		||||
                errorID(1633);
 | 
			
		||||
                return;
 | 
			
		||||
            }*/
 | 
			
		||||
            if (removeAt < 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            oldParent->_children.erase(oldParent->_children.begin() + removeAt);
 | 
			
		||||
            oldParent->updateSiblingIndex();
 | 
			
		||||
            oldParent->emit(NodeEventType::CHILD_REMOVED, this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (newParent) {
 | 
			
		||||
#if CC_DEBUG > 0
 | 
			
		||||
        if ((newParent->_objFlags & Flags::DEACTIVATING) == Flags::DEACTIVATING) {
 | 
			
		||||
            debug::errorID(3821);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        newParent->_children.emplace_back(this);
 | 
			
		||||
        _siblingIndex = static_cast<index_t>(newParent->_children.size() - 1);
 | 
			
		||||
        newParent->emit(NodeEventType::CHILD_ADDED, this);
 | 
			
		||||
    }
 | 
			
		||||
    onHierarchyChanged(oldParent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::walk(const WalkCallback &preFunc) {
 | 
			
		||||
    walk(preFunc, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::walk(const WalkCallback &preFunc, const WalkCallback &postFunc) { //NOLINT(misc-no-recursion)
 | 
			
		||||
    if (preFunc) {
 | 
			
		||||
        preFunc(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const auto &child : _children) {
 | 
			
		||||
        if (child) {
 | 
			
		||||
            child->walk(preFunc, postFunc);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (postFunc) {
 | 
			
		||||
        postFunc(this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//Component *Node::addComponent(Component *comp) {
 | 
			
		||||
//    comp->_node = this; // cjh TODO: shared_ptr
 | 
			
		||||
//    _components.emplace_back(comp);
 | 
			
		||||
//
 | 
			
		||||
//    if (isActiveInHierarchy()) {
 | 
			
		||||
//        NodeActivator::activateComp(comp);
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    return comp;
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
//void Node::removeComponent(Component *comp) {
 | 
			
		||||
//    auto iteComp = std::find(_components.begin(), _components.end(), comp);
 | 
			
		||||
//    if (iteComp != _components.end()) {
 | 
			
		||||
//        _components.erase(iteComp);
 | 
			
		||||
//    }
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
bool Node::onPreDestroyBase() {
 | 
			
		||||
    Flags destroyingFlag = Flags::DESTROYING;
 | 
			
		||||
    _objFlags |= destroyingFlag;
 | 
			
		||||
    bool destroyByParent = (!!_parent) && (!!(_parent->_objFlags & destroyingFlag));
 | 
			
		||||
#if CC_EDITOR
 | 
			
		||||
    if (!destroyByParent) {
 | 
			
		||||
        this->notifyEditorAttached(false);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (isPersistNode()) {
 | 
			
		||||
        emit(EventTypesToJS::NODE_REMOVE_PERSIST_ROOT_NODE);
 | 
			
		||||
    }
 | 
			
		||||
    if (!destroyByParent) {
 | 
			
		||||
        if (_parent) {
 | 
			
		||||
            emit(NodeEventType::PARENT_CHANGED, this);
 | 
			
		||||
            index_t childIdx = getIdxOfChild(_parent->_children, this);
 | 
			
		||||
            if (childIdx != -1) {
 | 
			
		||||
                _parent->_children.erase(_parent->_children.begin() + childIdx);
 | 
			
		||||
            }
 | 
			
		||||
            _siblingIndex = 0;
 | 
			
		||||
            _parent->updateSiblingIndex();
 | 
			
		||||
            _parent->emit(NodeEventType::CHILD_REMOVED, this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //NOTE: The following code is not needed now since we override Node._onPreDestroy in node.jsb.ts
 | 
			
		||||
    // and the logic will be done in TS.
 | 
			
		||||
    //    emit(NodeEventType::NODE_DESTROYED, this);
 | 
			
		||||
    //    for (const auto &child : _children) {
 | 
			
		||||
    //        child->destroyImmediate();
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    emit(EventTypesToJS::NODE_DESTROY_COMPONENTS);
 | 
			
		||||
 | 
			
		||||
    _eventProcessor->destroy();
 | 
			
		||||
    return destroyByParent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Node *Node::getChildByName(const ccstd::string &name) const {
 | 
			
		||||
    if (name.empty()) {
 | 
			
		||||
        CC_LOG_INFO("Invalid name");
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    for (const auto &child : _children) {
 | 
			
		||||
        if (child->_name == name) {
 | 
			
		||||
            return child;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setScene(Node *node) {
 | 
			
		||||
    node->updateScene();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::updateScene() {
 | 
			
		||||
    if (_parent == nullptr) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    _scene = _parent->_scene;
 | 
			
		||||
    emit(EventTypesToJS::NODE_SCENE_UPDATED, _scene);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* static */
 | 
			
		||||
index_t Node::getIdxOfChild(const ccstd::vector<IntrusivePtr<Node>> &child, Node *target) {
 | 
			
		||||
    auto iteChild = std::find(child.begin(), child.end(), target);
 | 
			
		||||
    if (iteChild != child.end()) {
 | 
			
		||||
        return static_cast<index_t>(iteChild - child.begin());
 | 
			
		||||
    }
 | 
			
		||||
    return CC_INVALID_INDEX;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Node *Node::getChildByUuid(const ccstd::string &uuid) const {
 | 
			
		||||
    if (uuid.empty()) {
 | 
			
		||||
        CC_LOG_INFO("Invalid uuid");
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    for (const auto &child : _children) {
 | 
			
		||||
        if (child->_id == uuid) {
 | 
			
		||||
            return child;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Node::isChildOf(Node *parent) const {
 | 
			
		||||
    const Node *child = this;
 | 
			
		||||
    do {
 | 
			
		||||
        if (child == parent) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        child = child->_parent;
 | 
			
		||||
    } while (child);
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::removeAllChildren() {
 | 
			
		||||
    for (auto i = static_cast<index_t>(_children.size() - 1); i >= 0; --i) {
 | 
			
		||||
        if (_children[i]) {
 | 
			
		||||
            _children[i]->setParent(nullptr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    _children.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setSiblingIndex(index_t index) {
 | 
			
		||||
    if (!_parent) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!!(_parent->_objFlags & Flags::DEACTIVATING)) {
 | 
			
		||||
        debug::errorID(3821);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    ccstd::vector<IntrusivePtr<Node>> &siblings = _parent->_children;
 | 
			
		||||
    index = index != -1 ? index : static_cast<index_t>(siblings.size()) - 1;
 | 
			
		||||
    index_t oldIdx = getIdxOfChild(siblings, this);
 | 
			
		||||
    if (index != oldIdx) {
 | 
			
		||||
        if (oldIdx != CC_INVALID_INDEX) {
 | 
			
		||||
            siblings.erase(siblings.begin() + oldIdx);
 | 
			
		||||
        }
 | 
			
		||||
        if (index < siblings.size()) {
 | 
			
		||||
            siblings.insert(siblings.begin() + index, this);
 | 
			
		||||
        } else {
 | 
			
		||||
            siblings.emplace_back(this);
 | 
			
		||||
        }
 | 
			
		||||
        _parent->updateSiblingIndex();
 | 
			
		||||
        if (onSiblingIndexChanged != nullptr) {
 | 
			
		||||
            onSiblingIndexChanged(index);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Node *Node::getChildByPath(const ccstd::string &path) const {
 | 
			
		||||
    size_t end = 0;
 | 
			
		||||
    ccstd::vector<ccstd::string> segments = StringUtil::split(path, "/");
 | 
			
		||||
    auto *lastNode = const_cast<Node *>(this);
 | 
			
		||||
    for (const ccstd::string &segment : segments) {
 | 
			
		||||
        if (segment.empty()) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        Node *next{nullptr};
 | 
			
		||||
        if (lastNode) {
 | 
			
		||||
            for (const auto &child : lastNode->_children) {
 | 
			
		||||
                if (child->_name == segment) {
 | 
			
		||||
                    next = child;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            lastNode = next;
 | 
			
		||||
        } else {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return lastNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
void Node::setPositionInternal(float x, float y, float z, bool calledFromJS) {
 | 
			
		||||
    _localPosition.set(x, y, z);
 | 
			
		||||
    invalidateChildren(TransformBit::POSITION);
 | 
			
		||||
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::POSITION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!calledFromJS) {
 | 
			
		||||
        notifyLocalPositionUpdated();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setRotationInternal(float x, float y, float z, float w, bool calledFromJS) {
 | 
			
		||||
    _localRotation.set(x, y, z, w);
 | 
			
		||||
    _eulerDirty = true;
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::ROTATION);
 | 
			
		||||
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!calledFromJS) {
 | 
			
		||||
        notifyLocalRotationUpdated();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setRotationFromEuler(float x, float y, float z) {
 | 
			
		||||
    _euler.set(x, y, z);
 | 
			
		||||
    Quaternion::fromEuler(x, y, z, &_localRotation);
 | 
			
		||||
    _eulerDirty = false;
 | 
			
		||||
    invalidateChildren(TransformBit::ROTATION);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyLocalRotationUpdated();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setScaleInternal(float x, float y, float z, bool calledFromJS) {
 | 
			
		||||
    _localScale.set(x, y, z);
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::SCALE);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::SCALE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!calledFromJS) {
 | 
			
		||||
        notifyLocalScaleUpdated();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::updateWorldTransform() { //NOLINT(misc-no-recursion)
 | 
			
		||||
    if (!getDirtyFlag()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    index_t i = 0;
 | 
			
		||||
    Node *curr = this;
 | 
			
		||||
    Mat3 mat3;
 | 
			
		||||
    Mat3 m43;
 | 
			
		||||
    Quaternion quat;
 | 
			
		||||
    while (curr && curr->getDirtyFlag()) {
 | 
			
		||||
        setDirtyNode(i++, curr);
 | 
			
		||||
        curr = curr->getParent();
 | 
			
		||||
    }
 | 
			
		||||
    Node *child{nullptr};
 | 
			
		||||
    uint32_t dirtyBits = 0;
 | 
			
		||||
    while (i) {
 | 
			
		||||
        child = getDirtyNode(--i);
 | 
			
		||||
        if (!child) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        dirtyBits |= child->getDirtyFlag();
 | 
			
		||||
        auto *currChild = child;
 | 
			
		||||
        if (curr) {
 | 
			
		||||
            if (dirtyBits & static_cast<uint32_t>(TransformBit::POSITION)) {
 | 
			
		||||
                currChild->_worldPosition.transformMat4(currChild->_localPosition, curr->_worldMatrix);
 | 
			
		||||
                currChild->_worldMatrix.m[12] = currChild->_worldPosition.x;
 | 
			
		||||
                currChild->_worldMatrix.m[13] = currChild->_worldPosition.y;
 | 
			
		||||
                currChild->_worldMatrix.m[14] = currChild->_worldPosition.z;
 | 
			
		||||
            }
 | 
			
		||||
            if (dirtyBits & static_cast<uint32_t>(TransformBit::RS)) {
 | 
			
		||||
                Mat4::fromRTS(currChild->_localRotation, currChild->_localPosition, currChild->_localScale, &currChild->_worldMatrix);
 | 
			
		||||
                Mat4::multiply(curr->_worldMatrix, currChild->_worldMatrix, &currChild->_worldMatrix);
 | 
			
		||||
                if (dirtyBits & static_cast<uint32_t>(TransformBit::ROTATION)) {
 | 
			
		||||
                    Quaternion::multiply(curr->_worldRotation, currChild->_localRotation, &currChild->_worldRotation);
 | 
			
		||||
                }
 | 
			
		||||
                quat = currChild->_worldRotation;
 | 
			
		||||
                quat.conjugate();
 | 
			
		||||
                Mat3::fromQuat(quat, &mat3);
 | 
			
		||||
                Mat3::fromMat4(currChild->_worldMatrix, &m43);
 | 
			
		||||
                Mat3::multiply(mat3, m43, &mat3);
 | 
			
		||||
                currChild->_worldScale.set(mat3.m[0], mat3.m[4], mat3.m[8]);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (child) {
 | 
			
		||||
            if (dirtyBits & static_cast<uint32_t>(TransformBit::POSITION)) {
 | 
			
		||||
                currChild->_worldPosition.set(currChild->_localPosition);
 | 
			
		||||
                currChild->_worldMatrix.m[12] = currChild->_worldPosition.x;
 | 
			
		||||
                currChild->_worldMatrix.m[13] = currChild->_worldPosition.y;
 | 
			
		||||
                currChild->_worldMatrix.m[14] = currChild->_worldPosition.z;
 | 
			
		||||
            }
 | 
			
		||||
            if (dirtyBits & static_cast<uint32_t>(TransformBit::RS)) {
 | 
			
		||||
                if (dirtyBits & static_cast<uint32_t>(TransformBit::ROTATION)) {
 | 
			
		||||
                    currChild->_worldRotation.set(currChild->_localRotation);
 | 
			
		||||
                }
 | 
			
		||||
                if (dirtyBits & static_cast<uint32_t>(TransformBit::SCALE)) {
 | 
			
		||||
                    currChild->_worldScale.set(currChild->_localScale);
 | 
			
		||||
                    Mat4::fromRTS(currChild->_worldRotation, currChild->_worldPosition, currChild->_worldScale, &currChild->_worldMatrix);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        child->setDirtyFlag(static_cast<uint32_t>(TransformBit::NONE));
 | 
			
		||||
        curr = child;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Mat4 &Node::getWorldMatrix() const { //NOLINT(misc-no-recursion)
 | 
			
		||||
    const_cast<Node *>(this)->updateWorldTransform();
 | 
			
		||||
    return _worldMatrix;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Mat4 Node::getWorldRS() {
 | 
			
		||||
    updateWorldTransform();
 | 
			
		||||
    Mat4 target{_worldMatrix};
 | 
			
		||||
    target.m[12] = target.m[13] = target.m[14] = 0;
 | 
			
		||||
    return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Mat4 Node::getWorldRT() {
 | 
			
		||||
    updateWorldTransform();
 | 
			
		||||
    Mat4 target;
 | 
			
		||||
    Mat4::fromRT(_worldRotation, _worldPosition, &target);
 | 
			
		||||
    return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::invalidateChildren(TransformBit dirtyBit) {
 | 
			
		||||
    auto curDirtyBit{static_cast<uint32_t>(dirtyBit)};
 | 
			
		||||
    const uint32_t childDirtyBit{curDirtyBit | static_cast<uint32_t>(TransformBit::POSITION)};
 | 
			
		||||
    setDirtyNode(0, this);
 | 
			
		||||
    int i{0};
 | 
			
		||||
    while (i >= 0) {
 | 
			
		||||
        Node *cur = getDirtyNode(i--);
 | 
			
		||||
        if (cur == nullptr) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const uint32_t hasChangedFlags = cur->getChangedFlags();
 | 
			
		||||
        if (cur->isValid() && (cur->getDirtyFlag() & hasChangedFlags & curDirtyBit) != curDirtyBit) {
 | 
			
		||||
            cur->setDirtyFlag(cur->getDirtyFlag() | curDirtyBit);
 | 
			
		||||
            cur->setChangedFlags(hasChangedFlags | curDirtyBit);
 | 
			
		||||
 | 
			
		||||
            for (Node *curChild : cur->getChildren()) {
 | 
			
		||||
                setDirtyNode(++i, curChild);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        curDirtyBit = childDirtyBit;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setWorldPosition(float x, float y, float z) {
 | 
			
		||||
    _worldPosition.set(x, y, z);
 | 
			
		||||
    if (_parent) {
 | 
			
		||||
        _parent->updateWorldTransform();
 | 
			
		||||
        Mat4 invertWMat{_parent->_worldMatrix};
 | 
			
		||||
        invertWMat.inverse();
 | 
			
		||||
        _localPosition.transformMat4(_worldPosition, invertWMat);
 | 
			
		||||
    } else {
 | 
			
		||||
        _localPosition.set(_worldPosition);
 | 
			
		||||
    }
 | 
			
		||||
    notifyLocalPositionUpdated();
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::POSITION);
 | 
			
		||||
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::POSITION);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Vec3 &Node::getWorldPosition() const {
 | 
			
		||||
    const_cast<Node *>(this)->updateWorldTransform();
 | 
			
		||||
    return _worldPosition;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setWorldRotation(float x, float y, float z, float w) {
 | 
			
		||||
    _worldRotation.set(x, y, z, w);
 | 
			
		||||
    if (_parent) {
 | 
			
		||||
        _parent->updateWorldTransform();
 | 
			
		||||
        _localRotation.set(_parent->_worldRotation.getConjugated());
 | 
			
		||||
        _localRotation.multiply(_worldRotation);
 | 
			
		||||
    } else {
 | 
			
		||||
        _localRotation.set(_worldRotation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _eulerDirty = true;
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::ROTATION);
 | 
			
		||||
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyLocalRotationUpdated();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Quaternion &Node::getWorldRotation() const { //NOLINT(misc-no-recursion)
 | 
			
		||||
    const_cast<Node *>(this)->updateWorldTransform();
 | 
			
		||||
    return _worldRotation;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setWorldScale(float x, float y, float z) {
 | 
			
		||||
    _worldScale.set(x, y, z);
 | 
			
		||||
    if (_parent != nullptr) {
 | 
			
		||||
        _parent->updateWorldTransform();
 | 
			
		||||
        Mat3 mat3;
 | 
			
		||||
        Mat3::fromQuat(_parent->_worldRotation.getConjugated(), &mat3);
 | 
			
		||||
        Mat3 b;
 | 
			
		||||
        Mat3::fromMat4(_parent->_worldMatrix, &b);
 | 
			
		||||
        Mat3::multiply(mat3, b, &mat3);
 | 
			
		||||
        Mat3 mat3Scaling;
 | 
			
		||||
        mat3Scaling.m[0] = _worldScale.x;
 | 
			
		||||
        mat3Scaling.m[4] = _worldScale.y;
 | 
			
		||||
        mat3Scaling.m[8] = _worldScale.z;
 | 
			
		||||
 | 
			
		||||
        mat3.inverse();
 | 
			
		||||
        Mat3::multiply(mat3Scaling, mat3, &mat3);
 | 
			
		||||
        _localScale.x = Vec3{mat3.m[0], mat3.m[1], mat3.m[2]}.length();
 | 
			
		||||
        _localScale.y = Vec3{mat3.m[3], mat3.m[4], mat3.m[5]}.length();
 | 
			
		||||
        _localScale.z = Vec3{mat3.m[6], mat3.m[7], mat3.m[8]}.length();
 | 
			
		||||
    } else {
 | 
			
		||||
        _localScale = _worldScale;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyLocalScaleUpdated();
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::SCALE);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::SCALE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Vec3 &Node::getWorldScale() const {
 | 
			
		||||
    const_cast<Node *>(this)->updateWorldTransform();
 | 
			
		||||
    return _worldScale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setForward(const Vec3 &dir) {
 | 
			
		||||
    const float len = dir.length();
 | 
			
		||||
    Vec3 v3Temp = dir * (-1.F / len);
 | 
			
		||||
    Quaternion qTemp{Quaternion::identity()};
 | 
			
		||||
    Quaternion::fromViewUp(v3Temp, &qTemp);
 | 
			
		||||
    setWorldRotation(qTemp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setAngle(float val) {
 | 
			
		||||
    _euler.set(0, 0, val);
 | 
			
		||||
    Quaternion::createFromAngleZ(val, &_localRotation);
 | 
			
		||||
    _eulerDirty = false;
 | 
			
		||||
    invalidateChildren(TransformBit::ROTATION);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyLocalRotationUpdated();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::onSetParent(Node *oldParent, bool keepWorldTransform) {
 | 
			
		||||
    if (_parent) {
 | 
			
		||||
        if ((oldParent == nullptr || oldParent->_scene != _parent->_scene) && _parent->_scene != nullptr) {
 | 
			
		||||
            walk(setScene);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (keepWorldTransform) {
 | 
			
		||||
        if (_parent) {
 | 
			
		||||
            _parent->updateWorldTransform();
 | 
			
		||||
            if (mathutils::approx<float>(_parent->_worldMatrix.determinant(), 0.F, mathutils::EPSILON)) {
 | 
			
		||||
                CC_LOG_WARNING("14300");
 | 
			
		||||
                _dirtyFlag |= static_cast<uint32_t>(TransformBit::TRS);
 | 
			
		||||
                updateWorldTransform();
 | 
			
		||||
            } else {
 | 
			
		||||
                Mat4 tmpMat4 = _parent->_worldMatrix.getInversed() * _worldMatrix;
 | 
			
		||||
                Mat4::toRTS(tmpMat4, &_localRotation, &_localPosition, &_localScale);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            _localPosition.set(_worldPosition);
 | 
			
		||||
            _localRotation.set(_worldRotation);
 | 
			
		||||
            _localScale.set(_worldScale);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        notifyLocalPositionRotationScaleUpdated();
 | 
			
		||||
        _eulerDirty = true;
 | 
			
		||||
    }
 | 
			
		||||
    invalidateChildren(TransformBit::TRS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::rotate(const Quaternion &rot, NodeSpace ns /* = NodeSpace::LOCAL*/, bool calledFromJS /* = false*/) {
 | 
			
		||||
    Quaternion qTempA{rot};
 | 
			
		||||
    qTempA.normalize();
 | 
			
		||||
    if (ns == NodeSpace::LOCAL) {
 | 
			
		||||
        _localRotation *= qTempA;
 | 
			
		||||
    } else if (ns == NodeSpace::WORLD) {
 | 
			
		||||
        Quaternion qTempB{Quaternion::identity()};
 | 
			
		||||
        qTempB = qTempA * getWorldRotation();
 | 
			
		||||
        qTempA = _worldRotation;
 | 
			
		||||
        qTempA.inverse();
 | 
			
		||||
        qTempB = qTempA * qTempB;
 | 
			
		||||
        _localRotation = _localRotation * qTempB;
 | 
			
		||||
    }
 | 
			
		||||
    _eulerDirty = true;
 | 
			
		||||
    invalidateChildren(TransformBit::ROTATION);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!calledFromJS) {
 | 
			
		||||
        notifyLocalRotationUpdated();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::lookAt(const Vec3 &pos, const Vec3 &up) {
 | 
			
		||||
    Vec3 vTemp = getWorldPosition();
 | 
			
		||||
    Quaternion qTemp{Quaternion::identity()};
 | 
			
		||||
    vTemp -= pos;
 | 
			
		||||
    vTemp.normalize();
 | 
			
		||||
    Quaternion::fromViewUp(vTemp, up, &qTemp);
 | 
			
		||||
    setWorldRotation(qTemp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vec3 Node::inverseTransformPoint(const Vec3 &p) {
 | 
			
		||||
    Vec3 out;
 | 
			
		||||
    out.set(p.x, p.y, p.z);
 | 
			
		||||
    Node *cur{this};
 | 
			
		||||
    index_t i{0};
 | 
			
		||||
    while (cur != nullptr && cur->getParent()) {
 | 
			
		||||
        setDirtyNode(i++, cur);
 | 
			
		||||
        cur = cur->getParent();
 | 
			
		||||
    }
 | 
			
		||||
    while (i >= 0) {
 | 
			
		||||
        Vec3::transformInverseRTS(out, cur->getRotation(), cur->getPosition(), cur->getScale(), &out);
 | 
			
		||||
        --i;
 | 
			
		||||
        cur = getDirtyNode(i);
 | 
			
		||||
    }
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setMatrix(const Mat4 &val) {
 | 
			
		||||
    val.decompose(&_localScale, &_localRotation, &_localPosition);
 | 
			
		||||
    notifyLocalPositionRotationScaleUpdated();
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::TRS);
 | 
			
		||||
    _eulerDirty = true;
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::TRS);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setWorldRotationFromEuler(float x, float y, float z) {
 | 
			
		||||
    Quaternion::fromEuler(x, y, z, &_worldRotation);
 | 
			
		||||
    if (_parent) {
 | 
			
		||||
        _parent->updateWorldTransform();
 | 
			
		||||
        _localRotation = _parent->_worldRotation.getConjugated() * _worldRotation;
 | 
			
		||||
    } else {
 | 
			
		||||
        _localRotation = _worldRotation;
 | 
			
		||||
    }
 | 
			
		||||
    _eulerDirty = true;
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::ROTATION);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::ROTATION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyLocalRotationUpdated();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::setRTSInternal(Quaternion *rot, Vec3 *pos, Vec3 *scale, bool calledFromJS) {
 | 
			
		||||
    uint32_t dirtyBit = 0;
 | 
			
		||||
    if (rot) {
 | 
			
		||||
        dirtyBit |= static_cast<uint32_t>(TransformBit::ROTATION);
 | 
			
		||||
        _localRotation = *rot;
 | 
			
		||||
        _eulerDirty = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (pos) {
 | 
			
		||||
        _localPosition = *pos;
 | 
			
		||||
        dirtyBit |= static_cast<uint32_t>(TransformBit::POSITION);
 | 
			
		||||
    }
 | 
			
		||||
    if (scale) {
 | 
			
		||||
        _localScale = *scale;
 | 
			
		||||
        dirtyBit |= static_cast<uint32_t>(TransformBit::SCALE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!calledFromJS) {
 | 
			
		||||
        notifyLocalPositionRotationScaleUpdated();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dirtyBit) {
 | 
			
		||||
        invalidateChildren(static_cast<TransformBit>(dirtyBit));
 | 
			
		||||
        if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
            emit(NodeEventType::TRANSFORM_CHANGED, dirtyBit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::resetChangedFlags() {
 | 
			
		||||
    globalFlagChangeVersion++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::clearNodeArray() {
 | 
			
		||||
    if (clearFrame < clearRound) {
 | 
			
		||||
        clearFrame++;
 | 
			
		||||
    } else {
 | 
			
		||||
        clearFrame = 0;
 | 
			
		||||
        dirtyNodes.clear();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ccstd::string Node::getPathInHierarchy() const {
 | 
			
		||||
    ccstd::string result = getName();
 | 
			
		||||
    Node *curNode = getParent();
 | 
			
		||||
    while (curNode && curNode->getParent()) {
 | 
			
		||||
        result.insert(0, "/").insert(0, curNode->getName());
 | 
			
		||||
        curNode = curNode->getParent();
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::translate(const Vec3 &trans, NodeSpace ns) {
 | 
			
		||||
    Vec3 v3Temp{trans};
 | 
			
		||||
    if (ns == NodeSpace::LOCAL) {
 | 
			
		||||
        v3Temp.transformQuat(_localRotation);
 | 
			
		||||
        _localPosition.x += v3Temp.x;
 | 
			
		||||
        _localPosition.y += v3Temp.y;
 | 
			
		||||
        _localPosition.z += v3Temp.z;
 | 
			
		||||
    } else if (ns == NodeSpace::WORLD) {
 | 
			
		||||
        if (_parent) {
 | 
			
		||||
            Quaternion qTemp = _parent->getWorldRotation();
 | 
			
		||||
            qTemp.inverse();
 | 
			
		||||
            v3Temp.transformQuat(qTemp);
 | 
			
		||||
            Vec3 scale{_worldScale};
 | 
			
		||||
            _localPosition.x += v3Temp.x / scale.x;
 | 
			
		||||
            _localPosition.y += v3Temp.y / scale.y;
 | 
			
		||||
            _localPosition.z += v3Temp.z / scale.z;
 | 
			
		||||
        } else {
 | 
			
		||||
            _localPosition.x += trans.x;
 | 
			
		||||
            _localPosition.y += trans.y;
 | 
			
		||||
            _localPosition.z += trans.z;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    notifyLocalPositionUpdated();
 | 
			
		||||
 | 
			
		||||
    invalidateChildren(TransformBit::POSITION);
 | 
			
		||||
    if (_eventMask & TRANSFORM_ON) {
 | 
			
		||||
        emit(NodeEventType::TRANSFORM_CHANGED, TransformBit::POSITION);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Node::onPreDestroy() {
 | 
			
		||||
    bool result = onPreDestroyBase();
 | 
			
		||||
    // TODO(Lenovo): bookOfChange free
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Node::onHierarchyChanged(Node *oldParent) {
 | 
			
		||||
    emit(EventTypesToJS::NODE_REATTACH);
 | 
			
		||||
    _eventProcessor->reattach();
 | 
			
		||||
    onHierarchyChangedBase(oldParent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* static */
 | 
			
		||||
//Node *Node::find(const ccstd::string &path, Node *referenceNode /* = nullptr*/) {
 | 
			
		||||
//    return cc::find(path, referenceNode);
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
// For deserialization
 | 
			
		||||
// void Node::_setChild(index_t i, Node *child) {
 | 
			
		||||
//    if (i < _children.size()) {
 | 
			
		||||
//        _children[i] = child;
 | 
			
		||||
//    } else {
 | 
			
		||||
//        CC_LOG_ERROR("Invalid index (%d) for Node children (size: %u)", i, static_cast<uint32_t>(_children.size()));
 | 
			
		||||
//    }
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
// Node *Node::_getChild(index_t i) {
 | 
			
		||||
//    if (i < _children.size()) {
 | 
			
		||||
//        return _children[i];
 | 
			
		||||
//    }
 | 
			
		||||
//    CC_LOG_ERROR("Invalid index (%d) for Node children (size: %u)", i, static_cast<uint32_t>(_children.size()));
 | 
			
		||||
//    return nullptr;
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
// void Node::_setChildrenSize(uint32_t size) {
 | 
			
		||||
//    _children.resize(size);
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
// uint32_t Node::_getChildrenSize() {
 | 
			
		||||
//    return _children.size();
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
void Node::_setChildren(ccstd::vector<IntrusivePtr<Node>> &&children) {
 | 
			
		||||
    _children = std::move(children);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
} // namespace cc
 | 
			
		||||
@@ -0,0 +1,861 @@
 | 
			
		||||
/****************************************************************************
 | 
			
		||||
 Copyright (c) 2021 Xiamen Yaji Software Co., Ltd.
 | 
			
		||||
 | 
			
		||||
 http://www.cocos.com
 | 
			
		||||
 | 
			
		||||
 Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 of this software and associated engine source code (the "Software"), a limited,
 | 
			
		||||
 worldwide, royalty-free, non-assignable, revocable and non-exclusive license
 | 
			
		||||
 to use Cocos Creator solely to develop games on your target platforms. You shall
 | 
			
		||||
 not use Cocos Creator software for developing other software or tools that's
 | 
			
		||||
 used for developing games. You are not granted to publish, distribute,
 | 
			
		||||
 sublicense, and/or sell copies of Cocos Creator.
 | 
			
		||||
 | 
			
		||||
 The software or tools in this License Agreement are licensed, not sold.
 | 
			
		||||
 Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
 | 
			
		||||
 | 
			
		||||
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 THE SOFTWARE.
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "base/Ptr.h"
 | 
			
		||||
#include "base/std/any.h"
 | 
			
		||||
#include "bindings/utils/BindingUtils.h"
 | 
			
		||||
//#include "core/components/Component.h"
 | 
			
		||||
//#include "core/event/Event.h"
 | 
			
		||||
#include "core/event/EventTypesToJS.h"
 | 
			
		||||
#include "core/scene-graph/BaseNode.h"
 | 
			
		||||
#include "core/scene-graph/Layers.h"
 | 
			
		||||
#include "core/scene-graph/NodeEnum.h"
 | 
			
		||||
#include "core/scene-graph/NodeEvent.h"
 | 
			
		||||
#include "core/scene-graph/NodeEventProcessor.h"
 | 
			
		||||
 | 
			
		||||
#include "math/Mat3.h"
 | 
			
		||||
#include "math/Mat4.h"
 | 
			
		||||
#include "math/Quaternion.h"
 | 
			
		||||
#include "math/Vec3.h"
 | 
			
		||||
#include "math/Vec4.h"
 | 
			
		||||
 | 
			
		||||
namespace cc {
 | 
			
		||||
 | 
			
		||||
class Scene;
 | 
			
		||||
class NodeEventProcessor;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Event types emitted by Node
 | 
			
		||||
 */
 | 
			
		||||
using EventType = NodeEventType;
 | 
			
		||||
/**
 | 
			
		||||
 * Bit masks for Node transformation parts
 | 
			
		||||
 */
 | 
			
		||||
using TransformDirtyBit = TransformBit;
 | 
			
		||||
 | 
			
		||||
class Node : public BaseNode {
 | 
			
		||||
public:
 | 
			
		||||
    class UserData : public RefCounted {
 | 
			
		||||
    public:
 | 
			
		||||
        ~UserData() override = default;
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        UserData() = default;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    using Super = BaseNode;
 | 
			
		||||
 | 
			
		||||
    static const uint32_t TRANSFORM_ON;
 | 
			
		||||
 | 
			
		||||
    static Node *instantiate(Node *cloned, bool isSyncedNode);
 | 
			
		||||
    static void setScene(Node *);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Finds a node by hierarchy path, the path is case-sensitive.
 | 
			
		||||
     * It will traverse the hierarchy by splitting the path using '/' character.
 | 
			
		||||
     * This function will still returns the node even if it is inactive.
 | 
			
		||||
     * It is recommended to not use this function every frame instead cache the result at startup.
 | 
			
		||||
     * @zh 通过路径从节点树中查找节点的方法,路径是大小写敏感的,并且通过 `/` 来分隔节点层级。
 | 
			
		||||
     * 即使节点的状态是未启用的也可以找到,建议将结果缓存,而不是每次需要都去查找。
 | 
			
		||||
     * @param path The path of the target node
 | 
			
		||||
     * @param referenceNode If given, the search will be limited in the sub node tree of the reference node
 | 
			
		||||
     */
 | 
			
		||||
    //    static Node *find(const ccstd::string &path, Node *referenceNode = nullptr);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Determine whether the given object is a normal Node. Will return false if [[Scene]] given.
 | 
			
		||||
     * @zh 指定对象是否是普通的节点?如果传入 [[Scene]] 会返回 false。
 | 
			
		||||
     */
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    static bool isNode(T *obj);
 | 
			
		||||
 | 
			
		||||
    static void resetChangedFlags();
 | 
			
		||||
    static void clearNodeArray();
 | 
			
		||||
 | 
			
		||||
    Node();
 | 
			
		||||
    explicit Node(const ccstd::string &name);
 | 
			
		||||
    ~Node() override;
 | 
			
		||||
 | 
			
		||||
    virtual void onPostActivated(bool active) {}
 | 
			
		||||
 | 
			
		||||
    void setParent(Node *parent, bool isKeepWorld = false);
 | 
			
		||||
 | 
			
		||||
    inline Scene *getScene() const { return _scene; };
 | 
			
		||||
 | 
			
		||||
    using WalkCallback = std::function<void(Node *)>;
 | 
			
		||||
    void walk(const WalkCallback &preFunc);
 | 
			
		||||
    void walk(const WalkCallback &preFunc, const WalkCallback &postFunc);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void on(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    void on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    void on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void once(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    void once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    void once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename LambdaType>
 | 
			
		||||
    std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
    once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    void off(const CallbacksInvoker::KeyType &type, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    void off(const CallbacksInvoker::KeyType &type, const CallbackID &cbID, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    void off(const CallbacksInvoker::KeyType &type, void *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    void off(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture = false);
 | 
			
		||||
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    void emit(const CallbacksInvoker::KeyType &type, Args &&...args);
 | 
			
		||||
 | 
			
		||||
    //    void dispatchEvent(event::Event *event);
 | 
			
		||||
    bool hasEventListener(const CallbacksInvoker::KeyType &type) const;
 | 
			
		||||
    bool hasEventListener(const CallbacksInvoker::KeyType &type, const CallbackID &cbID) const;
 | 
			
		||||
    bool hasEventListener(const CallbacksInvoker::KeyType &type, void *target) const;
 | 
			
		||||
    bool hasEventListener(const CallbacksInvoker::KeyType &type, void *target, const CallbackID &cbID) const;
 | 
			
		||||
 | 
			
		||||
    template <typename Target, typename... Args>
 | 
			
		||||
    bool hasEventListener(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target) const;
 | 
			
		||||
 | 
			
		||||
    void targetOff(const CallbacksInvoker::KeyType &type);
 | 
			
		||||
 | 
			
		||||
    bool destroy() override {
 | 
			
		||||
        if (CCObject::destroy()) {
 | 
			
		||||
            setActive(false);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void destroyAllChildren() {
 | 
			
		||||
        for (const auto &child : _children) {
 | 
			
		||||
            child->destroy();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void updateSiblingIndex() {
 | 
			
		||||
        index_t i = 0;
 | 
			
		||||
        for (const auto &child : _children) {
 | 
			
		||||
            child->_siblingIndex = i++;
 | 
			
		||||
        }
 | 
			
		||||
        emit(NodeEventType::SIBLING_ORDER_CHANGED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void addChild(Node *node) { node->setParent(this); }
 | 
			
		||||
 | 
			
		||||
    inline void removeChild(Node *node) const {
 | 
			
		||||
        auto idx = getIdxOfChild(_children, node);
 | 
			
		||||
        if (idx != -1) {
 | 
			
		||||
            node->setParent(nullptr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    inline void removeFromParent() {
 | 
			
		||||
        if (_parent) {
 | 
			
		||||
            _parent->removeChild(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    void removeAllChildren();
 | 
			
		||||
 | 
			
		||||
    bool isChildOf(Node *parent) const;
 | 
			
		||||
 | 
			
		||||
    void setActive(bool isActive);
 | 
			
		||||
 | 
			
		||||
    void setSiblingIndex(index_t index);
 | 
			
		||||
 | 
			
		||||
    inline bool isPersistNode() const {
 | 
			
		||||
        return static_cast<FlagBits>(_objFlags & Flags::DONT_DESTROY) > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void setPersistNode(bool val) {
 | 
			
		||||
        val ? _objFlags |= Flags::DONT_DESTROY : _objFlags &= ~Flags::DONT_DESTROY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline const ccstd::string &getUuid() const {
 | 
			
		||||
        return _id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline bool isActive() const { return _active != 0; }
 | 
			
		||||
 | 
			
		||||
    inline bool isActiveInHierarchy() const { return _activeInHierarchy != 0; }
 | 
			
		||||
    inline void setActiveInHierarchy(bool v) {
 | 
			
		||||
        _activeInHierarchy = (v ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline const ccstd::vector<IntrusivePtr<Node>> &getChildren() const { return _children; }
 | 
			
		||||
    inline Node *getParent() const { return _parent; }
 | 
			
		||||
    inline NodeEventProcessor *getEventProcessor() const { return _eventProcessor; }
 | 
			
		||||
 | 
			
		||||
    Node *getChildByUuid(const ccstd::string &uuid) const;
 | 
			
		||||
    Node *getChildByName(const ccstd::string &name) const;
 | 
			
		||||
    Node *getChildByPath(const ccstd::string &path) const;
 | 
			
		||||
    inline index_t getSiblingIndex() const { return _siblingIndex; }
 | 
			
		||||
    inline UserData *getUserData() { return _userData.get(); }
 | 
			
		||||
    inline void setUserData(UserData *data) { _userData = data; }
 | 
			
		||||
    inline void insertChild(Node *child, index_t siblingIndex) {
 | 
			
		||||
        child->setParent(this);
 | 
			
		||||
        child->setSiblingIndex(siblingIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void invalidateChildren(TransformBit dirtyBit);
 | 
			
		||||
 | 
			
		||||
    void translate(const Vec3 &, NodeSpace ns = NodeSpace::LOCAL);
 | 
			
		||||
    void rotate(const Quaternion &rot, NodeSpace ns = NodeSpace::LOCAL, bool calledFromJS = false);
 | 
			
		||||
    inline void rotateForJS(float x, float y, float z, float w, NodeSpace ns = NodeSpace::LOCAL) {
 | 
			
		||||
        rotate(Quaternion(x, y, z, w), ns, true);
 | 
			
		||||
    }
 | 
			
		||||
    void lookAt(const Vec3 &pos, const Vec3 &up = Vec3::UNIT_Y);
 | 
			
		||||
 | 
			
		||||
    void pauseSystemEvents(bool recursive) {}  // cjh TODO:
 | 
			
		||||
    void resumeSystemEvents(bool recursive) {} // cjh TODO:
 | 
			
		||||
 | 
			
		||||
    ccstd::string getPathInHierarchy() const;
 | 
			
		||||
    // ===============================
 | 
			
		||||
    // transform
 | 
			
		||||
    // ===============================
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set position in local coordinate system
 | 
			
		||||
     * @zh 设置本地坐标
 | 
			
		||||
     * @param position Target position
 | 
			
		||||
     */
 | 
			
		||||
    inline void setPosition(const Vec3 &pos) { setPosition(pos.x, pos.y, pos.z); }
 | 
			
		||||
    inline void setPosition(float x, float y) { setPosition(x, y, _localPosition.z); }
 | 
			
		||||
    inline void setPosition(float x, float y, float z) { setPositionInternal(x, y, z, false); }
 | 
			
		||||
    inline void setPositionInternal(float x, float y, bool calledFromJS) { setPositionInternal(x, y, _localPosition.z, calledFromJS); }
 | 
			
		||||
    void setPositionInternal(float x, float y, float z, bool calledFromJS);
 | 
			
		||||
    // It is invoked after deserialization. It only sets position value, not triggers other logic.
 | 
			
		||||
    inline void setPositionForJS(float x, float y, float z) { _localPosition.set(x, y, z); }
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get position in local coordinate system, please try to pass `out` vector and reuse it to avoid garbage.
 | 
			
		||||
     * @zh 获取本地坐标,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。
 | 
			
		||||
     * @param out Set the result to out vector
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    inline const Vec3 &getPosition() const { return _localPosition; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set rotation in local coordinate system with a quaternion representing the rotation
 | 
			
		||||
     * @zh 用四元数设置本地旋转
 | 
			
		||||
     * @param rotation Rotation in quaternion
 | 
			
		||||
     */
 | 
			
		||||
    inline void setRotation(const Quaternion &rotation) { setRotation(rotation.x, rotation.y, rotation.z, rotation.w); }
 | 
			
		||||
    inline void setRotation(float x, float y, float z, float w) { setRotationInternal(x, y, z, w, false); }
 | 
			
		||||
    void setRotationInternal(float x, float y, float z, float w, bool calledFromJS);
 | 
			
		||||
    inline void setRotationForJS(float x, float y, float z, float w) { _localRotation.set(x, y, z, w); }
 | 
			
		||||
 | 
			
		||||
    inline void setEulerAngles(const Vec3 &val) { setRotationFromEuler(val.x, val.y, val.z); }
 | 
			
		||||
    inline void setRotationFromEuler(const Vec3 &val) { setRotationFromEuler(val.x, val.y, val.z); }
 | 
			
		||||
    inline void setRotationFromEuler(float x, float y) { setRotationFromEuler(x, y, _euler.z); }
 | 
			
		||||
    void setRotationFromEuler(float x, float y, float z);
 | 
			
		||||
    inline void setRotationFromEulerForJS(float x, float y, float z) { _euler.set(x, y, z); }
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get rotation as quaternion in local coordinate system, please try to pass `out` quaternion and reuse it to avoid garbage.
 | 
			
		||||
     * @zh 获取本地旋转,注意,尽可能传递复用的 [[Quat]] 以避免产生垃圾。
 | 
			
		||||
     * @param out Set the result to out quaternion
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new quaternion will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    inline const Quaternion &getRotation() const { return _localRotation; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set scale in local coordinate system
 | 
			
		||||
     * @zh 设置本地缩放
 | 
			
		||||
     * @param scale Target scale
 | 
			
		||||
     */
 | 
			
		||||
    inline void setScale(const Vec3 &scale) { setScale(scale.x, scale.y, scale.z); }
 | 
			
		||||
    inline void setScale(float x, float y) { setScale(x, y, _localScale.z); }
 | 
			
		||||
    inline void setScale(float x, float y, float z) { setScaleInternal(x, y, z, false); }
 | 
			
		||||
    inline void setScaleInternal(float x, float y, bool calledFromJS) { setScaleInternal(x, y, _localScale.z, calledFromJS); }
 | 
			
		||||
    void setScaleInternal(float x, float y, float z, bool calledFromJS);
 | 
			
		||||
    inline void setScaleForJS(float x, float y, float z) { _localScale.set(x, y, z); }
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get scale in local coordinate system, please try to pass `out` vector and reuse it to avoid garbage.
 | 
			
		||||
     * @zh 获取本地缩放,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。
 | 
			
		||||
     * @param out Set the result to out vector
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    inline const Vec3 &getScale() const { return _localScale; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Inversely transform a point from world coordinate system to local coordinate system.
 | 
			
		||||
     * @zh 逆向变换一个空间点,一般用于将世界坐标转换到本地坐标系中。
 | 
			
		||||
     * @param p A position in world coordinate system
 | 
			
		||||
     * @return The result point in local coordinate system will be stored in this vector
 | 
			
		||||
     */
 | 
			
		||||
    Vec3 inverseTransformPoint(const Vec3 &p);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set position in world coordinate system
 | 
			
		||||
     * @zh 设置世界坐标
 | 
			
		||||
     * @param position Target position
 | 
			
		||||
     */
 | 
			
		||||
    inline void setWorldPosition(const Vec3 &pos) { setWorldPosition(pos.x, pos.y, pos.z); }
 | 
			
		||||
    void setWorldPosition(float x, float y, float z);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get position in world coordinate system, please try to pass `out` vector and reuse it to avoid garbage.
 | 
			
		||||
     * @zh 获取世界坐标,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。
 | 
			
		||||
     * @param out Set the result to out vector
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    const Vec3 &getWorldPosition() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set rotation in world coordinate system with a quaternion representing the rotation
 | 
			
		||||
     * @zh 用四元数设置世界坐标系下的旋转
 | 
			
		||||
     * @param rotation Rotation in quaternion
 | 
			
		||||
     */
 | 
			
		||||
    inline void setWorldRotation(const Quaternion &rotation) { setWorldRotation(rotation.x, rotation.y, rotation.z, rotation.w); }
 | 
			
		||||
    void setWorldRotation(float x, float y, float z, float w);
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get rotation as quaternion in world coordinate system, please try to pass `out` quaternion and reuse it to avoid garbage.
 | 
			
		||||
     * @zh 获取世界坐标系下的旋转,注意,尽可能传递复用的 [[Quat]] 以避免产生垃圾。
 | 
			
		||||
     * @param out Set the result to out quaternion
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new quaternion will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    const Quaternion &getWorldRotation() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set rotation in world coordinate system with euler angles
 | 
			
		||||
     * @zh 用欧拉角设置世界坐标系下的旋转
 | 
			
		||||
     * @param x X axis rotation
 | 
			
		||||
     * @param y Y axis rotation
 | 
			
		||||
     * @param z Z axis rotation
 | 
			
		||||
     */
 | 
			
		||||
    inline void setWorldScale(const Vec3 &scale) { setWorldScale(scale.x, scale.y, scale.z); }
 | 
			
		||||
    void setWorldScale(float x, float y, float z);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get scale in world coordinate system, please try to pass `out` vector and reuse it to avoid garbage.
 | 
			
		||||
     * @zh 获取世界缩放,注意,尽可能传递复用的 [[Vec3]] 以避免产生垃圾。
 | 
			
		||||
     * @param out Set the result to out vector
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new vector will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    const Vec3 &getWorldScale() const;
 | 
			
		||||
 | 
			
		||||
    void setWorldRotationFromEuler(float x, float y, float z);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Local transformation matrix
 | 
			
		||||
     * @zh 本地坐标系变换矩阵
 | 
			
		||||
     */
 | 
			
		||||
    void setMatrix(const Mat4 &val);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Update the world transform information if outdated
 | 
			
		||||
     * @zh 更新节点的世界变换信息
 | 
			
		||||
     */
 | 
			
		||||
    void updateWorldTransform();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get a world transform matrix
 | 
			
		||||
     * @zh 获取世界变换矩阵
 | 
			
		||||
     * @param out Set the result to out matrix
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new matrix will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    const Mat4 &getWorldMatrix() const;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get a world transform matrix with only rotation and scale
 | 
			
		||||
     * @zh 获取只包含旋转和缩放的世界变换矩阵
 | 
			
		||||
     * @param out Set the result to out matrix
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new matrix will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    Mat4 getWorldRS();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Get a world transform matrix with only rotation and translation
 | 
			
		||||
     * @zh 获取只包含旋转和位移的世界变换矩阵
 | 
			
		||||
     * @param out Set the result to out matrix
 | 
			
		||||
     * @return If `out` given, the return value equals to `out`, otherwise a new matrix will be generated and return
 | 
			
		||||
     */
 | 
			
		||||
    Mat4 getWorldRT();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Set local transformation with rotation, position and scale separately.
 | 
			
		||||
     * @zh 一次性设置所有局部变换(平移、旋转、缩放)信息
 | 
			
		||||
     * @param rot The rotation
 | 
			
		||||
     * @param pos The position
 | 
			
		||||
     * @param scale The scale
 | 
			
		||||
     */
 | 
			
		||||
    void setRTSInternal(Quaternion *rot, Vec3 *pos, Vec3 *scale, bool calledFromJS);
 | 
			
		||||
    inline void setRTS(Quaternion *rot, Vec3 *pos, Vec3 *scale) { setRTSInternal(rot, pos, scale, false); }
 | 
			
		||||
 | 
			
		||||
    void setForward(const Vec3 &dir);
 | 
			
		||||
 | 
			
		||||
    void setAngle(float);
 | 
			
		||||
 | 
			
		||||
    inline const Vec3 &getEulerAngles() {
 | 
			
		||||
        if (_eulerDirty) {
 | 
			
		||||
            Quaternion::toEuler(_localRotation, false, &_euler);
 | 
			
		||||
            _eulerDirty = false;
 | 
			
		||||
        }
 | 
			
		||||
        return _euler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline float getAngle() const {
 | 
			
		||||
        return _euler.z;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline Vec3 getForward() const {
 | 
			
		||||
        Vec3 forward{0, 0, -1};
 | 
			
		||||
        forward.transformQuat(getWorldRotation());
 | 
			
		||||
        return forward;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline Vec3 getUp() const {
 | 
			
		||||
        Vec3 up{0, 1, 0};
 | 
			
		||||
        up.transformQuat(getWorldRotation());
 | 
			
		||||
        return up;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline Vec3 getRight() const {
 | 
			
		||||
        Vec3 right{1, 0, 0};
 | 
			
		||||
        right.transformQuat(getWorldRotation());
 | 
			
		||||
        return right;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline bool isStatic() const {
 | 
			
		||||
        return _isStatic != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void setStatic(bool v) {
 | 
			
		||||
        _isStatic = v ? 1 : 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @en Whether the node's transformation have changed during the current frame.
 | 
			
		||||
     * @zh 这个节点的空间变换信息在当前帧内是否有变过?
 | 
			
		||||
     */
 | 
			
		||||
    inline uint32_t getChangedFlags() const {
 | 
			
		||||
        return _hasChangedFlagsVersion == globalFlagChangeVersion ? _hasChangedFlags : 0;
 | 
			
		||||
    }
 | 
			
		||||
    inline void setChangedFlags(uint32_t value) {
 | 
			
		||||
        _hasChangedFlagsVersion = globalFlagChangeVersion;
 | 
			
		||||
        _hasChangedFlags = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void setDirtyFlag(uint32_t value) { _dirtyFlag = value; }
 | 
			
		||||
    inline uint32_t getDirtyFlag() const { return _dirtyFlag; }
 | 
			
		||||
    inline void setLayer(uint32_t layer) {
 | 
			
		||||
        _layer = layer;
 | 
			
		||||
        emit(NodeEventType::LAYER_CHANGED, layer);
 | 
			
		||||
    }
 | 
			
		||||
    inline uint32_t getLayer() const { return _layer; }
 | 
			
		||||
 | 
			
		||||
    inline float getUISortingPriority() const { return _uiSortingPriority; }
 | 
			
		||||
 | 
			
		||||
    inline bool isUISortingEnabled() const { return _uiSortingEnabled != 0; }
 | 
			
		||||
 | 
			
		||||
    //    inline NodeUiProperties *getUIProps() const { return _uiProps.get(); }
 | 
			
		||||
 | 
			
		||||
    //    // ------------------  Component code start -----------------------------
 | 
			
		||||
    //    // TODO(Lenovo):
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename = std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    static Component *findComponent(Node * /*node*/) {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //        return nullptr;
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename = std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    static Component *findComponents(Node * /*node*/, const ccstd::vector<Component *> & /*components*/) {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //        return nullptr;
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename = std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    static Component *findChildComponent(const ccstd::vector<Node *> & /*children*/) {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //        return nullptr;
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename = std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    static void findChildComponents(const ccstd::vector<Node *> & /*children*/, ccstd::vector<Component *> & /*components*/) {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename = std::enable_if_t<std::is_base_of_v<Component, T>, T>>
 | 
			
		||||
    //    T *addComponent() {
 | 
			
		||||
    //        T *comp = new T();
 | 
			
		||||
    //        return static_cast<T *>(addComponent(comp));
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    void removeComponent() {
 | 
			
		||||
    //        for (auto iter = _components.begin(); iter != _components.end(); ++iter) {
 | 
			
		||||
    //            if (dynamic_cast<T *>(*iter) != nullptr) {
 | 
			
		||||
    //                _components.erase(iter);
 | 
			
		||||
    //            }
 | 
			
		||||
    //        }
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    Component *addComponent(Component *comp);
 | 
			
		||||
    //    void       removeComponent(Component *comp);
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename = std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    Component *getComponent() const {
 | 
			
		||||
    //        for (auto *component : _components) {
 | 
			
		||||
    //            if (dynamic_cast<T *>(component) != nullptr) {
 | 
			
		||||
    //                return component;
 | 
			
		||||
    //            }
 | 
			
		||||
    //        }
 | 
			
		||||
    //        return nullptr;
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    // TODO(Lenovo):
 | 
			
		||||
    //    template <typename T, typename std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    ccstd::vector<Component *> getComponents() const {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //        return {};
 | 
			
		||||
    //    };
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    Component *getComponentInChildren(const T & /*comp*/) const {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //        return nullptr;
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    template <typename T, typename std::enable_if_t<std::is_base_of<Component, T>::value>>
 | 
			
		||||
    //    ccstd::vector<Component *> getComponentsInChildren() const {
 | 
			
		||||
    //        // cjh TODO:
 | 
			
		||||
    //        CC_ASSERT(false);
 | 
			
		||||
    //        return {};
 | 
			
		||||
    //    }
 | 
			
		||||
    //
 | 
			
		||||
    //    inline ccstd::vector<Component *> getComponents() const { return _components; }
 | 
			
		||||
    //
 | 
			
		||||
    //    void                     checkMultipleComp(Component *comp) {}
 | 
			
		||||
    //    ccstd::vector<Component *> _components;
 | 
			
		||||
    //
 | 
			
		||||
    //    friend void componentCorrupted(Node *node, Component *comp, uint32_t index);
 | 
			
		||||
    // ------------------  Component code end -----------------------------
 | 
			
		||||
 | 
			
		||||
    // For deserialization
 | 
			
		||||
    //    void     _setChild(index_t i, Node *child);
 | 
			
		||||
    //    Node *   _getChild(index_t i);
 | 
			
		||||
    //    void     _setChildrenSize(uint32_t size);
 | 
			
		||||
    //    uint32_t _getChildrenSize();
 | 
			
		||||
    void _setChildren(ccstd::vector<IntrusivePtr<Node>> &&children); // NOLINT
 | 
			
		||||
 | 
			
		||||
    inline se::Object *_getSharedArrayBufferObject() const { return _sharedMemoryActor.getSharedArrayBufferObject(); } // NOLINT
 | 
			
		||||
 | 
			
		||||
    bool onPreDestroy() override;
 | 
			
		||||
    bool onPreDestroyBase();
 | 
			
		||||
 | 
			
		||||
    std::function<void(index_t)> onSiblingIndexChanged{nullptr};
 | 
			
		||||
    // For deserialization
 | 
			
		||||
    ccstd::string _id;
 | 
			
		||||
    Node *_parent{nullptr};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static index_t getIdxOfChild(const ccstd::vector<IntrusivePtr<Node>> &, Node *);
 | 
			
		||||
 | 
			
		||||
    virtual void onBatchCreated(bool dontChildPrefab);
 | 
			
		||||
    virtual void updateScene();
 | 
			
		||||
 | 
			
		||||
    void onSetParent(Node *oldParent, bool keepWorldTransform);
 | 
			
		||||
    void onHierarchyChanged(Node *);
 | 
			
		||||
    void onHierarchyChangedBase(Node *oldParent);
 | 
			
		||||
 | 
			
		||||
    inline void notifyLocalPositionUpdated() {
 | 
			
		||||
        emit(EventTypesToJS::NODE_LOCAL_POSITION_UPDATED, _localPosition.x, _localPosition.y, _localPosition.z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void notifyLocalRotationUpdated() {
 | 
			
		||||
        emit(EventTypesToJS::NODE_LOCAL_ROTATION_UPDATED, _localRotation.x, _localRotation.y, _localRotation.z, _localRotation.w);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void notifyLocalScaleUpdated() {
 | 
			
		||||
        emit(EventTypesToJS::NODE_LOCAL_SCALE_UPDATED, _localScale.x, _localScale.y, _localScale.z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void notifyLocalPositionRotationScaleUpdated() {
 | 
			
		||||
        emit(EventTypesToJS::NODE_LOCAL_POSITION_ROTATION_SCALE_UPDATED,
 | 
			
		||||
             _localPosition.x, _localPosition.y, _localPosition.z,
 | 
			
		||||
             _localRotation.x, _localRotation.y, _localRotation.z, _localRotation.w,
 | 
			
		||||
             _localScale.x, _localScale.y, _localScale.z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if CC_EDITOR
 | 
			
		||||
    inline void notifyEditorAttached(bool attached) {
 | 
			
		||||
        emit(EventTypesToJS::NODE_EDITOR_ATTACHED, attached);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // increase on every frame, used to identify the frame
 | 
			
		||||
    static uint32_t globalFlagChangeVersion;
 | 
			
		||||
 | 
			
		||||
    static uint32_t clearFrame;
 | 
			
		||||
    static uint32_t clearRound;
 | 
			
		||||
 | 
			
		||||
    Scene *_scene{nullptr};
 | 
			
		||||
    NodeEventProcessor *_eventProcessor{nullptr};
 | 
			
		||||
    IntrusivePtr<UserData> _userData;
 | 
			
		||||
 | 
			
		||||
    ccstd::vector<IntrusivePtr<Node>> _children;
 | 
			
		||||
    bindings::NativeMemorySharedToScriptActor _sharedMemoryActor;
 | 
			
		||||
    // local transform
 | 
			
		||||
    Vec3 _localPosition{Vec3::ZERO};
 | 
			
		||||
    Vec3 _localScale{Vec3::ONE};
 | 
			
		||||
    Quaternion _localRotation{Quaternion::identity()};
 | 
			
		||||
    // world transform
 | 
			
		||||
    Vec3 _worldPosition{Vec3::ZERO};
 | 
			
		||||
    Vec3 _worldScale{Vec3::ONE};
 | 
			
		||||
    Vec3 _euler{0, 0, 0};
 | 
			
		||||
    Quaternion _worldRotation{Quaternion::identity()};
 | 
			
		||||
    Mat4 _worldMatrix{Mat4::IDENTITY};
 | 
			
		||||
 | 
			
		||||
    // Shared memory with JS
 | 
			
		||||
    // NOTE: TypeArray created in node.jsb.ts _ctor should have the same memory layout
 | 
			
		||||
    uint32_t _eventMask{0};                                             // Uint32: 0
 | 
			
		||||
    uint32_t _layer{static_cast<uint32_t>(Layers::LayerList::DEFAULT)}; // Uint32: 1
 | 
			
		||||
    uint32_t _dirtyFlag{0};                                             // Uint32: 2
 | 
			
		||||
    index_t _siblingIndex{0};                                           // Int32: 0
 | 
			
		||||
    float _uiSortingPriority{0.0F};                                     // Float: 0
 | 
			
		||||
    uint8_t _activeInHierarchy{0};                                      // Uint8: 0
 | 
			
		||||
    uint8_t _active{1};                                                 // Uint8: 1
 | 
			
		||||
    uint8_t _isStatic{0};                                               // Uint8: 2
 | 
			
		||||
    uint8_t _uiSortingEnabled{0};                                         // Uint8: 3
 | 
			
		||||
 | 
			
		||||
    /* set _hasChangedFlagsVersion to globalFlagChangeVersion when `_hasChangedFlags` updated.
 | 
			
		||||
     * `globalFlagChangeVersion == _hasChangedFlagsVersion` means that "_hasChangedFlags is dirty in current frametime".
 | 
			
		||||
     */
 | 
			
		||||
    uint32_t _hasChangedFlagsVersion{0};
 | 
			
		||||
    uint32_t _hasChangedFlags{0};
 | 
			
		||||
 | 
			
		||||
    bool _eulerDirty{false};
 | 
			
		||||
 | 
			
		||||
    friend class NodeActivator;
 | 
			
		||||
    friend class Scene;
 | 
			
		||||
 | 
			
		||||
    CC_DISALLOW_COPY_MOVE_ASSIGN(Node);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
bool Node::isNode(T *obj) {
 | 
			
		||||
    return dynamic_cast<Node *>(obj) != nullptr && dynamic_cast<Scene *>(obj) == nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
void Node::emit(const CallbacksInvoker::KeyType &type, Args &&...args) {
 | 
			
		||||
    _eventProcessor->emit(type, std::forward<Args>(args)...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::on(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture) {
 | 
			
		||||
    if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
        _eventMask |= TRANSFORM_ON;
 | 
			
		||||
    }
 | 
			
		||||
    _eventProcessor->on(type, memberFn, target, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
void Node::on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
        _eventMask |= TRANSFORM_ON;
 | 
			
		||||
    }
 | 
			
		||||
    _eventProcessor->on(type, std::forward<std::function<void(Args...)>>(callback), cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
        _eventMask |= TRANSFORM_ON;
 | 
			
		||||
    }
 | 
			
		||||
    _eventProcessor->on(type, std::forward<std::function<void(Args...)>>(callback), target, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
        _eventMask |= TRANSFORM_ON;
 | 
			
		||||
    }
 | 
			
		||||
    _eventProcessor->on(type, callback, target, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
        _eventMask |= TRANSFORM_ON;
 | 
			
		||||
    }
 | 
			
		||||
    _eventProcessor->on(type, callback, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
void Node::on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    on(type, callback, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::on(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    on(type, callback, target, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    on(type, callback, target, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::on(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    on(type, callback, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::once(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture) {
 | 
			
		||||
    _eventProcessor->once(type, memberFn, target, useCapture);
 | 
			
		||||
}
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
void Node::once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    _eventProcessor->once(type, callback, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    _eventProcessor->once(type, std::forward<std::function<void(Args...)>>(callback), target, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    _eventProcessor->once(type, callback, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, CallbackID &cbID, bool useCapture) {
 | 
			
		||||
    _eventProcessor->once(type, callback, target, cbID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
void Node::once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    once(type, callback, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::once(const CallbacksInvoker::KeyType &type, std::function<void(Args...)> &&callback, Target *target, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    once(type, callback, target, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    once(type, callback, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename LambdaType>
 | 
			
		||||
std::enable_if_t<!std::is_member_function_pointer<LambdaType>::value, void>
 | 
			
		||||
Node::once(const CallbacksInvoker::KeyType &type, LambdaType &&callback, Target *target, bool useCapture) {
 | 
			
		||||
    CallbackID unusedID{0};
 | 
			
		||||
    once(type, callback, target, unusedID, useCapture);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
void Node::off(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target, bool useCapture) {
 | 
			
		||||
    _eventProcessor->off(type, memberFn, target, useCapture);
 | 
			
		||||
    bool hasListeners = _eventProcessor->hasEventListener(type);
 | 
			
		||||
    if (!hasListeners) {
 | 
			
		||||
        if (type == NodeEventType::TRANSFORM_CHANGED) {
 | 
			
		||||
            _eventMask &= ~TRANSFORM_ON;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Target, typename... Args>
 | 
			
		||||
bool Node::hasEventListener(const CallbacksInvoker::KeyType &type, void (Target::*memberFn)(Args...), Target *target) const {
 | 
			
		||||
    return _eventProcessor->hasEventListener(type, memberFn, target);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace cc
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
相信大部分人都做过UI渲染优化,其重点也就是合批。但是在当前cocos使用节点树深度遍历的方式中,想要把相同类型的组件放在一起,势必会修改节点树,很可能破坏掉优雅合理的节点组织结构;还有可能因为节点树的改动,需要添加一些冗余糟糕的代码。
 | 
			
		||||
最近刚好有时间,逛论坛的时候看到有同学提这类问题,各位大哥也给出了解决方案,就是在渲染的时候给节点重新排序。于是心血来潮去看了下3.6.3的源码,好像要实现其实挺简单的。
 | 
			
		||||
# 原理
 | 
			
		||||
为需要排序的UI渲染器设置排序优先级; 在UI渲染遍历节点树阶段,不立即执行各种UI渲染器,而是把UI渲染器缓存起来;在UI渲染遍历完节点树后,对UI渲染器缓存通过排序优先级进行排序后执行。然而,遮罩会打断这一过程,所以如果项目中大量使用了遮罩,优化效果可能会不太明显。
 | 
			
		||||
为需要排序的UI渲染器(Sprite或者Label等)设置排序优先级; 在UI渲染遍历节点树阶段,不立即执行各种UI渲染器,而是把UI渲染器缓存起来;在UI渲染遍历完节点树后,对UI渲染器缓存通过排序优先级进行排序后执行。然而,遮罩会打断这一过程,所以如果项目中大量使用了遮罩,优化效果可能会不太明显。
 | 
			
		||||
# 效果对比
 | 
			
		||||
在ScrollView下生成200个项目测试对比。
 | 
			
		||||
 | 
			
		||||
@@ -68,8 +68,11 @@ export enum SortingLayer {
 | 
			
		||||
 */
 | 
			
		||||
export const ORDER_IN_LAYER_MAX = 100000;
 | 
			
		||||
```
 | 
			
		||||
4. 在需要排序的UI渲染器上(Sprite或者Label等),添加SortingGroup组件,并设置排序层和排序值  
 | 
			
		||||
和Unity不同的是`Order In Layer`不必是整数,这里可以使用小数。
 | 
			
		||||
4. 在需要排序的UI节点上,添加`SortingGroup`组件,并设置排序层和排序值,其配置会应用于当前节点和子孙节点上的UI渲染器;  
 | 
			
		||||
 | 
			
		||||
    UI渲染器的优先级以当前节点或者最近的祖先节点上的`SortingGroup`组件配置为准,如果没有则默认为`0`。
 | 
			
		||||
 | 
			
		||||
    和Unity不同的是`Order In Layer`不必是整数,这里可以使用小数。
 | 
			
		||||
不同`Sorting Layer`的情况下,`Sorting Layer`枚举越小越先渲染;相同`Sorting Layer`的情况下,`Order In Layer`的值越小越先渲染。
 | 
			
		||||
 | 
			
		||||
# 注意
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user