From 56dd18b9836e09d49b3878d2ec6ac56d9ff85144 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Tue, 12 Aug 2025 11:08:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BA=9F=E5=BC=83core.scene=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E4=B8=BAsetscene=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/core-concepts.md | 2 +- docs/getting-started.md | 8 +-- docs/scene-management-guide.md | 20 ++++--- docs/system-guide.md | 2 +- .../assets/scripts/ecs/ECSManager.ts | 2 +- package-lock.json | 4 +- packages/core/package.json | 2 +- packages/core/src/Core.ts | 35 ++++++++++-- packages/core/src/ECS/IScene.ts | 6 +- packages/core/src/ECS/Scene.ts | 56 ------------------- packages/core/tests/Core.test.ts | 8 +++ packages/core/tests/ECS/Scene.test.ts | 34 +++++++++++ 13 files changed, 96 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index d100a963..a733f7be 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ import { Core, Scene, Entity, Component, EntitySystem, Matcher, Time } from '@es // 创建核心实例 const core = Core.create({ debug: true }); const scene = new Scene(); -Core.scene = scene; +Core.setScene(scene); // 定义组件 class PositionComponent extends Component { diff --git a/docs/core-concepts.md b/docs/core-concepts.md index 0d6e526f..90d320dc 100644 --- a/docs/core-concepts.md +++ b/docs/core-concepts.md @@ -107,7 +107,7 @@ const scene = new Scene(); scene.name = "GameScene"; // 设置为当前场景 -Core.scene = scene; +Core.setScene(scene; // 场景生命周期 scene.begin(); // 开始场景 diff --git a/docs/getting-started.md b/docs/getting-started.md index d5ab8b3b..7bf12888 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -189,7 +189,7 @@ class LayaECSGame extends LayaScene { this.ecsScene = new ECSScene(); this.ecsScene.name = "LayaGameScene"; - Core.scene = this.ecsScene; + Core.setScene(this.ecsScene); this.entityManager = new EntityManager(); this.setupSystems(); @@ -264,7 +264,7 @@ export class ECSGameManager extends CocosComponent { this.ecsScene = new ECSScene(); this.ecsScene.name = "CocosGameScene"; - Core.scene = this.ecsScene; + Core.setScene(this.ecsScene); this.entityManager = new EntityManager(); this.setupSystems(); @@ -340,7 +340,7 @@ class ServerGameManager { this.scene = new Scene(); this.scene.name = "ServerScene"; - Core.scene = this.scene; + Core.setScene(this.scene); this.entityManager = new EntityManager(); this.setupSystems(); @@ -434,7 +434,7 @@ class BrowserGame { this.scene = new Scene(); this.scene.name = "BrowserScene"; - Core.scene = this.scene; + Core.setScene(this.scene); this.entityManager = new EntityManager(); this.setupSystems(); diff --git a/docs/scene-management-guide.md b/docs/scene-management-guide.md index 390b2c4a..a8a3d1fc 100644 --- a/docs/scene-management-guide.md +++ b/docs/scene-management-guide.md @@ -19,10 +19,12 @@ import { Scene, Core } from '@esengine/ecs-framework'; // 创建场景 const gameScene = new Scene(); -// 设置为当前活动场景 -Core.scene = gameScene; +// 设置为当前活动场景(推荐使用setScene方法) +Core.setScene(gameScene); ``` +> **注意**: `Core.scene = ` 设置方式已被标记为废弃,推荐使用 `Core.setScene()` 方法。新方法提供更好的类型安全性和可预测的激活时序。 + ### 场景的生命周期 ```typescript @@ -113,7 +115,7 @@ class MenuScene extends Scene { private transitionToGame() { // 切换到游戏场景 const gameScene = new GameScene(); - Core.scene = gameScene; + Core.setScene(gameScene); } } ``` @@ -251,7 +253,7 @@ class SceneManager { // 创建新场景 this.currentScene = this.createScene(sceneType, data); - Core.scene = this.currentScene; + Core.setScene(this.currentScene); console.log(`切换到场景: ${sceneType}`); } @@ -265,7 +267,7 @@ class SceneManager { } this.currentScene = previousScene; - Core.scene = this.currentScene; + Core.setScene(this.currentScene); return true; } return false; @@ -280,7 +282,7 @@ class SceneManager { } this.currentScene = this.createScene(sceneType, data); - Core.scene = this.currentScene; + Core.setScene(this.currentScene); } popScene() { @@ -291,7 +293,7 @@ class SceneManager { this.currentScene = this.sceneHistory.pop()!; this.resumeScene(this.currentScene); - Core.scene = this.currentScene; + Core.setScene(this.currentScene); } } @@ -359,7 +361,7 @@ class TransitionManager { // 切换场景 fromScene.onDestroy(); - Core.scene = toScene; + Core.setScene(toScene); // 淡入新场景 await this.fadeIn(overlay, duration / 2); @@ -382,7 +384,7 @@ class TransitionManager { // 切换场景 fromScene.onDestroy(); - Core.scene = toScene; + Core.setScene(toScene); // 从相反方向滑入新场景 await this.slideScene(toScene, -slideDistance); diff --git a/docs/system-guide.md b/docs/system-guide.md index 150e4623..afb775c7 100644 --- a/docs/system-guide.md +++ b/docs/system-guide.md @@ -491,7 +491,7 @@ if (renderSystem) { } // 设置为当前场景 -Core.scene = scene; +Core.setScene(scene; ``` ### 系统的启用和禁用 diff --git a/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts b/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts index 50d0d91d..bdc394a5 100644 --- a/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts +++ b/extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts @@ -71,7 +71,7 @@ export class ECSManager extends Component { const gameScene = new GameScene(); // 3. 设置为当前场景(会自动调用scene.begin()) - Core.scene = gameScene; + Core.setScene(gameScene); this.isInitialized = true; // ECS框架初始化完成 diff --git a/package-lock.json b/package-lock.json index bb16b667..a616b1a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11400,7 +11400,7 @@ }, "packages/core": { "name": "@esengine/ecs-framework", - "version": "2.1.31", + "version": "2.1.34", "license": "MIT", "devDependencies": { "@rollup/plugin-commonjs": "^28.0.3", @@ -11532,7 +11532,7 @@ }, "packages/network-shared": { "name": "@esengine/ecs-framework-network-shared", - "version": "1.0.14", + "version": "1.0.15", "license": "MIT", "dependencies": { "protobufjs": "^7.5.3", diff --git a/packages/core/package.json b/packages/core/package.json index f8ceff0f..3535068c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@esengine/ecs-framework", - "version": "2.1.31", + "version": "2.1.34", "description": "用于Laya、Cocos Creator等JavaScript游戏引擎的高性能ECS框架", "type": "module", "main": "bin/index.js", diff --git a/packages/core/src/Core.ts b/packages/core/src/Core.ts index 3b2a7130..6e98098f 100644 --- a/packages/core/src/Core.ts +++ b/packages/core/src/Core.ts @@ -205,11 +205,23 @@ export class Core { } /** - * 设置当前活动的场景 + * 设置当前场景(已废弃) + * + * @deprecated 请使用 Core.setScene() 方法代替。scene setter 可能导致场景延迟激活的时序问题, + * 而 setScene() 提供更好的类型安全性和可预测的激活时序。 + * + * 迁移示例: + * ```typescript + * // 旧方式(已废弃) + * Core.scene = myScene; + * + * // 新方式(推荐) + * Core.setScene(myScene); + * ``` * * 如果当前没有场景,会立即切换;否则会在下一帧切换。 * - * @param value - 要设置的场景实例 + * @param value - 场景实例 */ public static set scene(value: IScene | null) { if (!value) return; @@ -222,10 +234,24 @@ export class Core { } /** - * 类型安全的场景设置方法 + * 类型安全的场景设置方法(推荐) + * + * 这是设置场景的推荐方法,提供更好的类型安全性和可预测的激活时序。 + * 相比于 scene setter,此方法能确保场景正确初始化和激活。 + * + * 如果当前没有场景,会立即切换;否则会在下一帧切换。 * * @param scene - 要设置的场景实例 - * @returns 设置的场景实例 + * @returns 设置的场景实例,便于链式调用 + * + * @example + * ```typescript + * const myScene = new MyScene(); + * Core.setScene(myScene); + * + * // 链式调用 + * const scene = Core.setScene(new MyScene()).addSystem(new MySystem()); + * ``` */ public static setScene(scene: T): T { if (this._instance._scene == null) { @@ -447,6 +473,7 @@ export class Core { private setSceneInternal(scene: IScene): void { this._scene = scene; this.onSceneChanged(); + this._scene.initialize(); this._scene.begin(); } diff --git a/packages/core/src/ECS/IScene.ts b/packages/core/src/ECS/IScene.ts index 798753ea..3fd72cf1 100644 --- a/packages/core/src/ECS/IScene.ts +++ b/packages/core/src/ECS/IScene.ts @@ -16,7 +16,7 @@ export interface IScene { /** * 场景名称 */ - readonly name: string; + name: string; /** * 场景中的实体集合 @@ -147,10 +147,6 @@ export interface ISceneConfig { * 场景名称 */ name?: string; - /** - * 是否自动开始 - */ - autoStart?: boolean; /** * 调试配置 */ diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 6bf59f39..91ab3502 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -14,27 +14,6 @@ import { IScene, ISceneConfig } from './IScene'; * * 实现IScene接口,提供场景的基础功能。 * 推荐使用组合而非继承的方式来构建自定义场景。 - * - * @example - * ```typescript - * // 推荐的组合方式 - * class GameScene implements IScene { - * private scene = new Scene(); - * - * public initialize(): void { - * this.scene.initialize(); - * // 自定义初始化逻辑 - * } - * } - * - * // 仍然支持继承方式 - * class GameScene extends Scene { - * public initialize(): void { - * super.initialize(); - * // 自定义逻辑 - * } - * } - * ``` */ export class Scene implements IScene { /** @@ -98,10 +77,6 @@ export class Scene implements IScene { return this.entityProcessors.processors; } - /** - * 是否已完成基础初始化 - */ - private _isBaseInitialized = false; /** * 创建场景实例 @@ -128,16 +103,6 @@ export class Scene implements IScene { this.eventSystem.emitSync('component:added', data); }); } - - // 标记基础初始化完成 - this._isBaseInitialized = true; - - // 立即调用初始化,但确保在基础设施就绪后 - this.initialize(); - - if (config?.autoStart) { - this.begin(); - } } /** @@ -284,27 +249,6 @@ export class Scene implements IScene { return entities; } - /** - * 批量创建实体 - * @param count 要创建的实体数量 - * @param namePrefix 实体名称前缀 - * @returns 创建的实体列表 - */ - public createEntitiesOld(count: number, namePrefix: string = "Entity"): Entity[] { - const entities: Entity[] = []; - - // 批量创建实体,延迟缓存清理 - for (let i = 0; i < count; i++) { - const entity = new Entity(`${namePrefix}_${i}`, this.identifierPool.checkOut()); - entities.push(entity); - this.addEntity(entity, true); // 延迟缓存清理 - } - - // 最后统一清理缓存 - this.querySystem.clearCache(); - - return entities; - } /** * 从场景中删除所有实体 diff --git a/packages/core/tests/Core.test.ts b/packages/core/tests/Core.test.ts index e1b43d45..b0f1b23b 100644 --- a/packages/core/tests/Core.test.ts +++ b/packages/core/tests/Core.test.ts @@ -137,6 +137,14 @@ describe('Core - 核心管理系统测试', () => { expect(testScene.beginCalled).toBe(true); }); + test('应该能够使用推荐的setScene方法设置场景', () => { + const scene = Core.setScene(testScene); + + expect(Core.scene).toBe(testScene); + expect(testScene.beginCalled).toBe(true); + expect(scene).toBe(testScene); // 应该返回场景实例 + }); + test('设置新场景应该触发场景切换', () => { const firstScene = new TestScene(); const secondScene = new TestScene(); diff --git a/packages/core/tests/ECS/Scene.test.ts b/packages/core/tests/ECS/Scene.test.ts index 17211100..ec93cd7a 100644 --- a/packages/core/tests/ECS/Scene.test.ts +++ b/packages/core/tests/ECS/Scene.test.ts @@ -142,6 +142,27 @@ describe('Scene - 场景管理系统测试', () => { expect(scene.entities.count).toBe(0); expect(scene.entityProcessors.count).toBe(0); }); + + test('应该能够使用配置创建场景', () => { + const configScene = new Scene({ name: "ConfigScene" }); + expect(configScene.name).toBe("ConfigScene"); + }); + + test('应该能够获取调试信息', () => { + scene.name = "DebugScene"; + scene.createEntity("TestEntity"); + scene.addEntityProcessor(new MovementSystem()); + + const debugInfo = scene.getDebugInfo(); + + expect(debugInfo.name).toBe("Scene"); + expect(debugInfo.entityCount).toBe(1); + expect(debugInfo.processorCount).toBe(1); + expect(debugInfo.isRunning).toBe(false); + expect(debugInfo.entities).toBeDefined(); + expect(debugInfo.processors).toBeDefined(); + expect(debugInfo.componentStats).toBeDefined(); + }); }); describe('实体管理', () => { @@ -446,6 +467,19 @@ describe('Scene - 场景管理系统测试', () => { expect(stats.queryStats.totalQueries).toBeGreaterThan(0); expect(parseFloat(stats.cacheStats.hitRate)).toBeGreaterThanOrEqual(0); }); + + test('应该能够压缩组件存储', () => { + // 创建一些实体和组件 + const entities = scene.createEntities(10, "Entity"); + entities.forEach(entity => { + entity.addComponent(new PositionComponent(Math.random() * 100, Math.random() * 100)); + }); + + // 压缩组件存储应该不抛出异常 + expect(() => { + scene.compactComponentStorage(); + }).not.toThrow(); + }); }); describe('内存管理和性能', () => {