Browse Source

feat: add SceneManager with full lifecycle management

master
npmrun 3 weeks ago
parent
commit
673c50ce8f
  1. 195
      src/scene/SceneManager.ts

195
src/scene/SceneManager.ts

@ -0,0 +1,195 @@
import { Container } from "pixi.js";
import Game from "@/core/Game";
import { logger } from "@/core/Logger";
import { assetManager } from "@/core/AssetManager";
import { SceneType } from "@/enums/SceneType";
import { BaseScene } from "./BaseScene";
import type { SceneConfig, IBaseScene } from "./types";
type StageChangeCallback = (
current: IBaseScene,
previous: IBaseScene | undefined
) => Promise<void> | void;
class SceneManager {
private static instance: SceneManager;
private game: Game;
private scenes: Map<string, IBaseScene> = new Map();
private _currentScene: IBaseScene | null = null;
private changeCallbacks: StageChangeCallback[] = [];
private constructor() {
this.game = Game.getInstance();
}
static getInstance(): SceneManager {
if (!SceneManager.instance) {
SceneManager.instance = new SceneManager();
}
return SceneManager.instance;
}
get currentScene(): IBaseScene | null {
return this._currentScene;
}
/** 监听场景变化 */
onStageChange(cb: StageChangeCallback): () => void {
this.changeCallbacks.push(cb);
return () => {
const index = this.changeCallbacks.indexOf(cb);
if (index >= 0) {
this.changeCallbacks.splice(index, 1);
}
};
}
/** 触发场景变化回调 */
private async emitStageChange(
current: IBaseScene,
previous: IBaseScene | undefined
): Promise<void> {
for (const cb of this.changeCallbacks) {
await cb(current, previous);
}
}
/** 注册场景 */
registerScene(scene: IBaseScene): void {
if (this.scenes.has(scene.name)) {
logger.warn(`SceneManager: scene "${scene.name}" already registered`);
}
this.scenes.set(scene.name, scene);
// 注入 changeScene 方法
scene.changeScene = (name: string, options) => {
this.changeScene(name, options);
};
if (scene.type === SceneType.Resident) {
this.game.stage.addChild(scene.stage);
logger.debug(`SceneManager: registered resident scene "${scene.name}"`);
} else {
logger.debug(`SceneManager: registered normal scene "${scene.name}"`);
}
}
/** 初始化入口场景 */
initScene(name: string): void {
const scene = this.getSceneOrThrow(name);
this._currentScene = scene;
if (scene.type === SceneType.Normal) {
this.game.stage.addChild(scene.stage);
}
this.emitStageChange(scene, undefined);
logger.debug(`SceneManager: initialized scene "${name}"`);
}
/** 切换场景 */
async changeScene(name: string, options?: { isHolderLast?: boolean }): Promise<void> {
const previous = this._currentScene;
if (!previous) {
throw new Error(`SceneManager: no current scene, call initScene first`);
}
const target = this.getSceneOrThrow(name);
// 处理前一个场景
if (previous.type === SceneType.Normal) {
if (options?.isHolderLast) {
previous.stage.visible = false;
// @ts-ignore 标记保留
previous.stage._isHolderLast = true;
} else {
// 销毁场景
previous.stage.destroy({ children: true });
this.game.stage.removeChild(previous.stage);
// 卸载资源
if (previous._assetsLoaded) {
await previous.unLoadBundle?.();
previous._assetsLoaded = false;
}
await previous.onUnLoad?.();
this.scenes.delete(previous.name);
}
} else if (previous.type === SceneType.Resident) {
previous.stage.visible = false;
await previous.onUnLoad?.();
}
this._currentScene = target;
// 处理目标场景
if (target.type === SceneType.Normal) {
// @ts-ignore
if (target.stage._isHolderLast) {
target.stage.visible = true;
} else {
this.game.stage.addChild(target.stage);
}
} else if (target.type === SceneType.Resident) {
target.stage.visible = true;
}
// 生命周期
if (!target._assetsLoaded) {
await target.loadBundle?.();
target._assetsLoaded = true;
}
if (!target._layoutDone || target.stage !== this.getSceneOrThrow(name).stage) {
await target.layout?.();
target._layoutDone = true;
}
await target.onLoad?.();
// 触发回调
await this.emitStageChange(target, previous);
logger.debug(`SceneManager: changed from "${previous?.name}" to "${name}"`);
}
/** 获取已注册场景 */
getScene(name: string): IBaseScene | undefined {
return this.scenes.get(name);
}
getSceneOrThrow(name: string): IBaseScene {
const scene = this.scenes.get(name);
if (!scene) {
throw new Error(`SceneManager: scene "${name}" not registered`);
}
return scene;
}
/** 确保场景存在,不存在则创建容器 */
ensureSceneExists(name: string, type: SceneType = SceneType.Normal): Container {
if (this.scenes.has(name)) {
return this.getSceneOrThrow(name).stage;
}
const container = new Container();
container.label = name;
// 动态创建一个简单场景
// 这个功能主要用于兼容旧的代码风格
class DynamicScene extends BaseScene {
stage = container;
name = name;
type = type;
}
const scene = new DynamicScene();
this.registerScene(scene);
return container;
}
hasScene(name: string): boolean {
return this.scenes.has(name);
}
}
export default SceneManager;
export const sceneManager = SceneManager.getInstance();
Loading…
Cancel
Save