From a7750c2894ce31c7bc6c3144ec39e2a99f3dd910 Mon Sep 17 00:00:00 2001 From: LING YE Date: Wed, 15 Oct 2025 15:48:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96=E6=97=B6=E5=AD=90=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E4=B8=A2=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在场景反序列化过程中,子实体虽然保持了父子引用关系, 但未被添加到 Scene 的实体集合和查询系统中,导致查询时子实体"丢失"。 --- .../src/ECS/Serialization/SceneSerializer.ts | 25 +++- .../ParentChildSerialization.test.ts | 117 ++++++++++++++++++ 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 packages/core/tests/ECS/Serialization/ParentChildSerialization.test.ts diff --git a/packages/core/src/ECS/Serialization/SceneSerializer.ts b/packages/core/src/ECS/Serialization/SceneSerializer.ts index 605f2fc2..6397bf97 100644 --- a/packages/core/src/ECS/Serialization/SceneSerializer.ts +++ b/packages/core/src/ECS/Serialization/SceneSerializer.ts @@ -275,15 +275,38 @@ export class SceneSerializer { // 将实体添加到场景 for (const entity of entities) { - scene.addEntity(entity); + scene.addEntity(entity, true); + this.addChildrenRecursively(entity, scene); } + // 统一清理缓存(批量操作完成后) + scene.querySystem.clearCache(); + scene.clearSystemEntityCaches(); + // 反序列化场景自定义数据 if (serializedScene.sceneData) { this.deserializeSceneData(serializedScene.sceneData, scene.sceneData); } } + /** + * 递归添加实体的所有子实体到场景 + * + * 修复反序列化时子实体丢失的问题: + * EntitySerializer.deserialize会提前设置子实体的scene引用, + * 导致Entity.addChild的条件判断(!child.scene)跳过scene.addEntity调用。 + * 因此需要在SceneSerializer中统一递归添加所有子实体。 + * + * @param entity 父实体 + * @param scene 目标场景 + */ + private static addChildrenRecursively(entity: Entity, scene: IScene): void { + for (const child of entity.children) { + scene.addEntity(child, true); // 延迟缓存清理 + this.addChildrenRecursively(child, scene); // 递归处理子实体的子实体 + } + } + /** * 序列化场景自定义数据 * diff --git a/packages/core/tests/ECS/Serialization/ParentChildSerialization.test.ts b/packages/core/tests/ECS/Serialization/ParentChildSerialization.test.ts new file mode 100644 index 00000000..69c9c809 --- /dev/null +++ b/packages/core/tests/ECS/Serialization/ParentChildSerialization.test.ts @@ -0,0 +1,117 @@ +/** + * 父子实体序列化和反序列化测试 + * + * 测试场景序列化和反序列化时父子实体关系的正确性 + */ + +import { Scene } from '../../../src'; + +describe('父子实体序列化测试', () => { + let scene: Scene; + + beforeEach(() => { + scene = new Scene({ name: 'TestScene' }); + }); + + afterEach(() => { + scene.end(); + }); + + test('应该正确反序列化父子实体层次结构', () => { + // 创建父实体 + const parent = scene.createEntity('parent'); + parent.tag = 100; + + // 创建2个子实体 + const child1 = scene.createEntity('child1'); + child1.tag = 200; + parent.addChild(child1); + + const child2 = scene.createEntity('child2'); + child2.tag = 200; + parent.addChild(child2); + + // 创建1个顶层实体(对照组) + const topLevel = scene.createEntity('topLevel'); + topLevel.tag = 200; + + // 验证序列化前的状态 + expect(scene.querySystem.queryAll().entities.length).toBe(4); + expect(scene.findEntitiesByTag(100).length).toBe(1); + expect(scene.findEntitiesByTag(200).length).toBe(3); + + // 序列化 + const serialized = scene.serialize({ format: 'json' }); + + // 创建新场景并反序列化 + const scene2 = new Scene({ name: 'LoadTestScene' }); + scene2.deserialize(serialized as string, { + strategy: 'replace', + preserveIds: true, + }); + + // 验证所有实体都被正确恢复 + const allEntities = scene2.querySystem.queryAll().entities; + expect(allEntities.length).toBe(4); + expect(scene2.findEntitiesByTag(100).length).toBe(1); + expect(scene2.findEntitiesByTag(200).length).toBe(3); + + // 验证父子关系正确恢复 + const restoredParent = scene2.findEntity('parent'); + expect(restoredParent).not.toBeNull(); + expect(restoredParent!.children.length).toBe(2); + + const restoredChild1 = scene2.findEntity('child1'); + const restoredChild2 = scene2.findEntity('child2'); + expect(restoredChild1).not.toBeNull(); + expect(restoredChild2).not.toBeNull(); + expect(restoredChild1!.parent).toBe(restoredParent); + expect(restoredChild2!.parent).toBe(restoredParent); + + scene2.end(); + }); + + test('应该正确反序列化多层级实体层次结构', () => { + // 创建多层级实体结构:grandparent -> parent -> child + const grandparent = scene.createEntity('grandparent'); + grandparent.tag = 1; + + const parent = scene.createEntity('parent'); + parent.tag = 2; + grandparent.addChild(parent); + + const child = scene.createEntity('child'); + child.tag = 3; + parent.addChild(child); + + expect(scene.querySystem.queryAll().entities.length).toBe(3); + + // 序列化 + const serialized = scene.serialize({ format: 'json' }); + + // 创建新场景并反序列化 + const scene2 = new Scene({ name: 'LoadTestScene' }); + scene2.deserialize(serialized as string, { + strategy: 'replace', + preserveIds: true, + }); + + // 验证多层级结构正确恢复 + expect(scene2.querySystem.queryAll().entities.length).toBe(3); + + const restoredGrandparent = scene2.findEntity('grandparent'); + const restoredParent = scene2.findEntity('parent'); + const restoredChild = scene2.findEntity('child'); + + expect(restoredGrandparent).not.toBeNull(); + expect(restoredParent).not.toBeNull(); + expect(restoredChild).not.toBeNull(); + + expect(restoredParent!.parent).toBe(restoredGrandparent); + expect(restoredChild!.parent).toBe(restoredParent); + expect(restoredGrandparent!.children.length).toBe(1); + expect(restoredParent!.children.length).toBe(1); + + scene2.end(); + }); +});