From 045d6193bb41b1b1e913392deaf1cadc4fcaae3a Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Wed, 22 Apr 2026 19:24:47 +0800 Subject: [PATCH 1/2] fix: clear children bug --- packages/core/src/Entity.ts | 7 +++++++ tests/src/core/Entity.test.ts | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/core/src/Entity.ts b/packages/core/src/Entity.ts index 9470d05817..4ee6796939 100644 --- a/packages/core/src/Entity.ts +++ b/packages/core/src/Entity.ts @@ -403,6 +403,11 @@ export class Entity extends EngineObject { for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; child._parent = null; + child._siblingIndex = -1; + // Dispatch `Child` to the old parent before `_processInActive` (which unregisters + // UI listeners via `cleanRootCanvas`), so subscribers such as UICanvas can react + // to the hierarchy change while still attached. + this._dispatchModify(EntityModifyFlags.Child, this); let activeChangeFlag = ActiveChangeFlag.None; child._isActiveInHierarchy && (activeChangeFlag |= ActiveChangeFlag.Hierarchy); @@ -410,6 +415,8 @@ export class Entity extends EngineObject { activeChangeFlag && child._processInActive(activeChangeFlag); Entity._traverseSetOwnerScene(child, null); // Must after child._processInActive(). + + child._setParentChange(); } children.length = 0; } diff --git a/tests/src/core/Entity.test.ts b/tests/src/core/Entity.test.ts index 0a428f4b60..edfed9fe4b 100644 --- a/tests/src/core/Entity.test.ts +++ b/tests/src/core/Entity.test.ts @@ -328,8 +328,29 @@ describe("Entity", async () => { child.parent = parent; const child2 = new Entity(engine, "child2"); child2.parent = parent; + + const parentModifyCount = [0, 0, 0]; + const childModifyCount = [0, 0, 0]; + const child2ModifyCount = [0, 0, 0]; + // @ts-ignore + parent._registerModifyListener((flag: EntityModifyFlags) => ++parentModifyCount[flag]); + // @ts-ignore + child._registerModifyListener((flag: EntityModifyFlags) => ++childModifyCount[flag]); + // @ts-ignore + child2._registerModifyListener((flag: EntityModifyFlags) => ++child2ModifyCount[flag]); + parent.clearChildren(); expect(parent.children.length).eq(0); + + // Parent should receive a single `Child` modify event for the whole clear so + // listeners (e.g. UICanvas) can invalidate their cached state. + expect(parentModifyCount[EntityModifyFlags.Child]).eq(1); + // Each detached child should receive a `Parent` modify event. + expect(childModifyCount[EntityModifyFlags.Parent]).eq(1); + expect(child2ModifyCount[EntityModifyFlags.Parent]).eq(1); + // Sibling index must be reset so the entity is treated as lonely afterwards. + expect(child.siblingIndex).eq(-1); + expect(child2.siblingIndex).eq(-1); }); it("sibling index", () => { const root = scene.createRootEntity(); From 44e725781826253c6ad8e99d1d4a9f8e89cee148 Mon Sep 17 00:00:00 2001 From: cptbtptpbcptdtptp Date: Mon, 20 Apr 2026 15:58:33 +0800 Subject: [PATCH 2/2] fix: entity set sibling error when without parent --- packages/core/src/Entity.ts | 8 +++----- packages/core/src/SceneManager.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/core/src/Entity.ts b/packages/core/src/Entity.ts index 4ee6796939..2419f4c401 100644 --- a/packages/core/src/Entity.ts +++ b/packages/core/src/Entity.ts @@ -9,7 +9,7 @@ import { Script } from "./Script"; import { Transform } from "./Transform"; import { UpdateFlagManager } from "./UpdateFlagManager"; import { ReferResource } from "./asset/ReferResource"; -import { EngineObject } from "./base"; +import { EngineObject, Logger } from "./base"; import { CloneUtils } from "./clone/CloneUtils"; import { ComponentCloner } from "./clone/ComponentCloner"; import { ActiveChangeFlag } from "./enums/ActiveChangeFlag"; @@ -212,16 +212,14 @@ export class Entity extends EngineObject { } set siblingIndex(value: number) { - if (this._siblingIndex === -1) { - throw `The entity ${this.name} is not in the hierarchy`; - } - if (this._isRoot) { this._setSiblingIndex(this._scene._rootEntities, value); } else { const parent = this._parent; this._setSiblingIndex(parent._children, value); parent._dispatchModify(EntityModifyFlags.Child, parent); + } else { + Logger.warn(`The entity ${this.name} is not in the hierarchy`); } } diff --git a/packages/core/src/SceneManager.ts b/packages/core/src/SceneManager.ts index 92de60d66b..984850fed4 100644 --- a/packages/core/src/SceneManager.ts +++ b/packages/core/src/SceneManager.ts @@ -94,7 +94,7 @@ export class SceneManager { const scenePromise = this.engine.resourceManager.load({ url, type: AssetType.Scene }); scenePromise.then((scene: Scene) => { if (destroyOldScene) { - const scenes = this._scenes.getArray(); + const scenes = this._scenes.getLoopArray(); for (let i = 0, n = scenes.length; i < n; i++) { scenes[i].destroy(); }