1 changed files with 195 additions and 0 deletions
@ -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…
Reference in new issue