SortingGroup组件可以应用于当前节点或者其子节点上的UI渲染器

This commit is contained in:
lujun 2023-02-08 00:33:52 +08:00
parent a120d33b79
commit d4658cabc6
17 changed files with 2597 additions and 239 deletions

View 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();
}
}

View File

@ -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": {}

View File

@ -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;
}
}

View File

@ -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
});
}

View 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
});
}

View File

@ -0,0 +1,9 @@
{
"ver": "4.0.23",
"importer": "typescript",
"imported": true,
"uuid": "421d8e4a-74d5-4851-a739-de22c13f87e3",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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
}
},
{

View File

@ -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"
)

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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组件并设置排序层和排序值 ![QQ截图20230205173334|524x176](./docs/images/QQ截图20230205173334.png)
和Unity不同的是`Order In Layer`不必是整数,这里可以使用小数。
4. 在需要排序的UI节点上添加`SortingGroup`组件并设置排序层和排序值其配置会应用于当前节点和子孙节点上的UI渲染器 ![QQ截图20230205173334|524x176](./docs/images/QQ截图20230205173334.png)
UI渲染器的优先级以当前节点或者最近的祖先节点上的`SortingGroup`组件配置为准,如果没有则默认为`0`。
和Unity不同的是`Order In Layer`不必是整数,这里可以使用小数。
不同`Sorting Layer`的情况下,`Sorting Layer`枚举越小越先渲染;相同`Sorting Layer`的情况下,`Order In Layer`的值越小越先渲染。
# 注意