275 lines
7.2 KiB
TypeScript
275 lines
7.2 KiB
TypeScript
import { IScene } from './IScene';
|
||
import { ECSFluentAPI, createECSAPI } from './Core/FluentAPI';
|
||
import { Time } from '../Utils/Time';
|
||
import { createLogger } from '../Utils/Logger';
|
||
import type { IService } from '../Core/ServiceContainer';
|
||
import { World } from './World';
|
||
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
|
||
|
||
/**
|
||
* 单场景管理器
|
||
*
|
||
* 适用场景:
|
||
* - 单人游戏
|
||
* - 简单场景切换
|
||
* - 不需要多World隔离的项目
|
||
*
|
||
* 特点:
|
||
* - 轻量级,零额外开销
|
||
* - 简单直观的API
|
||
* - 支持延迟场景切换
|
||
* - 自动管理ECS API
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 初始化Core
|
||
* Core.create({ debug: true });
|
||
*
|
||
* // 创建场景管理器
|
||
* const sceneManager = new SceneManager();
|
||
*
|
||
* // 设置场景
|
||
* class GameScene extends Scene {
|
||
* initialize() {
|
||
* const player = this.createEntity('Player');
|
||
* player.addComponent(new Transform(100, 100));
|
||
* }
|
||
* }
|
||
*
|
||
* sceneManager.setScene(new GameScene());
|
||
*
|
||
* // 游戏循环
|
||
* function gameLoop(deltaTime: number) {
|
||
* Core.update(deltaTime); // 更新全局服务
|
||
* sceneManager.update(); // 更新场景
|
||
* }
|
||
*
|
||
* // 延迟切换场景(下一帧生效)
|
||
* sceneManager.loadScene(new MenuScene());
|
||
* ```
|
||
*/
|
||
export class SceneManager implements IService {
|
||
/**
|
||
* 内部默认World
|
||
*/
|
||
private _defaultWorld: World;
|
||
|
||
/**
|
||
* 待切换的下一个场景(延迟切换用)
|
||
*/
|
||
private _nextScene: IScene | null = null;
|
||
|
||
/**
|
||
* ECS流式API
|
||
*/
|
||
private _ecsAPI: ECSFluentAPI | null = null;
|
||
|
||
/**
|
||
* 日志器
|
||
*/
|
||
private _logger = createLogger('SceneManager');
|
||
|
||
/**
|
||
* 场景切换回调函数
|
||
*/
|
||
private _onSceneChangedCallback?: () => void;
|
||
|
||
/**
|
||
* 性能监控器(从 Core 注入)
|
||
*/
|
||
private _performanceMonitor: PerformanceMonitor | null = null;
|
||
|
||
/**
|
||
* 默认场景ID
|
||
*/
|
||
private static readonly DEFAULT_SCENE_ID = '__main__';
|
||
|
||
constructor(performanceMonitor?: PerformanceMonitor) {
|
||
this._defaultWorld = new World({ name: '__default__' });
|
||
this._defaultWorld.start();
|
||
this._performanceMonitor = performanceMonitor || null;
|
||
}
|
||
|
||
/**
|
||
* 设置场景切换回调
|
||
*
|
||
* @param callback 场景切换时的回调函数
|
||
* @internal
|
||
*/
|
||
public setSceneChangedCallback(callback: () => void): void {
|
||
this._onSceneChangedCallback = callback;
|
||
}
|
||
|
||
/**
|
||
* 设置当前场景(立即切换)
|
||
*
|
||
* 会自动处理旧场景的结束和新场景的初始化。
|
||
*
|
||
* @param scene - 要设置的场景实例
|
||
* @returns 返回设置的场景实例,便于链式调用
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const gameScene = sceneManager.setScene(new GameScene());
|
||
* console.log(gameScene.name); // 可以立即使用返回的场景
|
||
* ```
|
||
*/
|
||
public setScene<T extends IScene>(scene: T): T {
|
||
// 移除旧场景
|
||
this._defaultWorld.removeAllScenes();
|
||
|
||
// 注册全局 PerformanceMonitor 到 Scene 的 ServiceContainer
|
||
if (this._performanceMonitor) {
|
||
scene.services.registerInstance(PerformanceMonitor, this._performanceMonitor);
|
||
}
|
||
|
||
// 通过 World 创建新场景
|
||
this._defaultWorld.createScene(SceneManager.DEFAULT_SCENE_ID, scene);
|
||
this._defaultWorld.setSceneActive(SceneManager.DEFAULT_SCENE_ID, true);
|
||
|
||
// 重建ECS API
|
||
if (scene.querySystem && scene.eventSystem) {
|
||
this._ecsAPI = createECSAPI(scene, scene.querySystem, scene.eventSystem);
|
||
} else {
|
||
this._ecsAPI = null;
|
||
}
|
||
|
||
// 触发场景切换回调
|
||
Time.sceneChanged();
|
||
|
||
// 通知调试管理器(通过回调)
|
||
if (this._onSceneChangedCallback) {
|
||
this._onSceneChangedCallback();
|
||
}
|
||
|
||
this._logger.info(`Scene changed to: ${scene.name}`);
|
||
|
||
return scene;
|
||
}
|
||
|
||
/**
|
||
* 延迟加载场景(下一帧切换)
|
||
*
|
||
* 场景不会立即切换,而是在下一次调用 update() 时切换。
|
||
* 这对于避免在当前帧的中途切换场景很有用。
|
||
*
|
||
* @param scene - 要加载的场景实例
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* // 在某个System中触发场景切换
|
||
* class GameOverSystem extends EntitySystem {
|
||
* process(entities: readonly Entity[]) {
|
||
* if (playerHealth <= 0) {
|
||
* sceneManager.loadScene(new GameOverScene());
|
||
* // 当前帧继续执行,场景将在下一帧切换
|
||
* }
|
||
* }
|
||
* }
|
||
* ```
|
||
*/
|
||
public loadScene<T extends IScene>(scene: T): void {
|
||
this._nextScene = scene;
|
||
this._logger.info(`Scheduled scene load: ${scene.name}`);
|
||
}
|
||
|
||
/**
|
||
* 获取当前活跃的场景
|
||
*
|
||
* @returns 当前场景实例,如果没有场景则返回null
|
||
*/
|
||
public get currentScene(): IScene | null {
|
||
return this._defaultWorld.getScene(SceneManager.DEFAULT_SCENE_ID);
|
||
}
|
||
|
||
/**
|
||
* 获取ECS流式API
|
||
*
|
||
* 提供便捷的实体查询、事件发射等功能。
|
||
*
|
||
* @returns ECS API实例,如果当前没有场景则返回null
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* const api = sceneManager.api;
|
||
* if (api) {
|
||
* // 查询所有敌人
|
||
* const enemies = api.find(Enemy, Transform);
|
||
*
|
||
* // 发射事件
|
||
* api.emit('game:start', { level: 1 });
|
||
* }
|
||
* ```
|
||
*/
|
||
public get api(): ECSFluentAPI | null {
|
||
return this._ecsAPI;
|
||
}
|
||
|
||
/**
|
||
* 更新场景
|
||
*
|
||
* 应该在每帧的游戏循环中调用。
|
||
* 会自动处理延迟场景切换。
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* function gameLoop(deltaTime: number) {
|
||
* Core.update(deltaTime);
|
||
* sceneManager.update(); // 每帧调用
|
||
* }
|
||
* ```
|
||
*/
|
||
public update(): void {
|
||
// 处理延迟场景切换
|
||
if (this._nextScene) {
|
||
this.setScene(this._nextScene);
|
||
this._nextScene = null;
|
||
}
|
||
|
||
// 通过 World 统一更新
|
||
this._defaultWorld.updateGlobalSystems();
|
||
this._defaultWorld.updateScenes();
|
||
}
|
||
|
||
/**
|
||
* 销毁场景管理器
|
||
*
|
||
* 会自动结束当前场景并清理所有资源。
|
||
* 通常在应用程序关闭时调用。
|
||
*/
|
||
public destroy(): void {
|
||
this._logger.info('SceneManager destroying');
|
||
|
||
this._defaultWorld.destroy();
|
||
this._nextScene = null;
|
||
this._ecsAPI = null;
|
||
|
||
this._logger.info('SceneManager destroyed');
|
||
}
|
||
|
||
/**
|
||
* 检查是否有活跃场景
|
||
*
|
||
* @returns 如果有活跃场景返回true,否则返回false
|
||
*/
|
||
public get hasScene(): boolean {
|
||
return this._defaultWorld.getScene(SceneManager.DEFAULT_SCENE_ID) !== null;
|
||
}
|
||
|
||
/**
|
||
* 检查是否有待切换的场景
|
||
*
|
||
* @returns 如果有待切换场景返回true,否则返回false
|
||
*/
|
||
public get hasPendingScene(): boolean {
|
||
return this._nextScene !== null;
|
||
}
|
||
|
||
/**
|
||
* 释放资源(IService接口)
|
||
*/
|
||
public dispose(): void {
|
||
this.destroy();
|
||
}
|
||
}
|