Browse Source

refactor: 去除旧 Game 管线,统一 BaseScene 与 changeScene

- init: 仅注册默认导出的场景类;简化 glob 加载逻辑
- SceneManager: 移除 changeStage 别名,保留 changeScene
- page_init / welcome2 / 0global: 对齐新场景与 API 调用

Made-with: Cursor
master
npmrun 3 weeks ago
parent
commit
77590eeae0
  1. 55
      src/init.ts
  2. 39
      src/scene/SceneManager.ts
  3. 75
      src/stages/_global/page_0global.ts
  4. 2
      src/stages/page_init.ts
  5. 4
      src/stages/welcome2/page_welcome2.ts

55
src/init.ts

@ -1,10 +1,12 @@
import Game from "./core/Game"; import Game from "./core/Game";
import SceneManager from "./scene/SceneManager"; import SceneManager from "./scene/SceneManager";
import { BaseScene } from "./scene/BaseScene"; import { BaseScene } from "./scene/BaseScene";
import type { IBaseScene } from "./scene/types";
import { SceneType } from "./enums/SceneType"; import { SceneType } from "./enums/SceneType";
import { assetManager } from "./core/AssetManager"; import { assetManager } from "./core/AssetManager";
import { logger } from "./core/Logger"; import { logger } from "./core/Logger";
import eventBus from "./core/EventBus"; import eventBus from "./core/EventBus";
import { Container } from "pixi.js";
const game = Game.getInstance(); const game = Game.getInstance();
const sceneManager = SceneManager.getInstance(); const sceneManager = SceneManager.getInstance();
@ -12,6 +14,14 @@ const sceneManager = SceneManager.getInstance();
// 定义构造函数类型 // 定义构造函数类型
type Constructor<T> = new (...args: any[]) => T; type Constructor<T> = new (...args: any[]) => T;
/** defineWindow() 返回的旧版模块形状 */
type DefineWindowModule = {
abstractClass: Constructor<IBaseScene & Record<string, unknown>>;
name?: string;
/** 与 SceneType 数值一致(旧 EP) */
type?: SceneType;
};
export async function initApp(): Promise<void> { export async function initApp(): Promise<void> {
await assetManager.init(); await assetManager.init();
await game.init(); await game.init();
@ -25,17 +35,52 @@ export async function initApp(): Promise<void> {
// 文件名匹配提取场景名称 // 文件名匹配提取场景名称
const match = path.match(/page_(.*?)\.ts$/); const match = path.match(/page_(.*?)\.ts$/);
if (!match) continue; if (!match) continue;
const fileSceneName = match[1];
const raw = (mod as { default: unknown }).default;
let SceneCtor: Constructor<IBaseScene & Record<string, unknown>>;
let defineWindowName: string | undefined;
let defineWindowType: SceneType | undefined;
if (typeof raw === "function") {
SceneCtor = raw as Constructor<IBaseScene & Record<string, unknown>>;
} else if (
raw &&
typeof raw === "object" &&
"abstractClass" in raw &&
typeof (raw as DefineWindowModule).abstractClass === "function"
) {
const dw = raw as DefineWindowModule;
SceneCtor = dw.abstractClass;
defineWindowName = dw.name;
defineWindowType = dw.type;
} else {
logger.warn(`initApp: invalid scene file ${path}, skipping`);
continue;
}
const sceneClass = (mod as { default: Constructor<BaseScene> }).default; const scene = new SceneCtor();
const scene = new sceneClass();
// 如果模块默认导出不是场景构造函数,尝试其他格式(兼容旧定义)
if (!scene || typeof scene !== "object" || !("stage" in scene)) { if (!scene || typeof scene !== "object" || !("stage" in scene)) {
logger.warn(`initApp: invalid scene file ${path}, skipping`); logger.warn(`initApp: invalid scene file ${path}, skipping`);
continue; continue;
} }
sceneManager.registerScene(scene); const legacy = scene as IBaseScene &
Record<string, unknown> & { name?: string; type?: SceneType; _type?: SceneType };
// defineWindow / PWindow 实例缺少只读字段的运行时值,用可变写入兼容旧场景
const mutable = legacy as { name?: string; type?: SceneType; stage: Container | null };
if (!mutable.name) {
mutable.name = defineWindowName ?? fileSceneName;
}
if (mutable.type === undefined) {
mutable.type = defineWindowType ?? legacy._type ?? SceneType.Normal;
}
if (!mutable.stage || mutable.stage === null) {
mutable.stage = new Container();
}
sceneManager.registerScene(legacy as IBaseScene);
} catch (error) { } catch (error) {
logger.error(`initApp: failed to load scene file ${path}`, error); logger.error(`initApp: failed to load scene file ${path}`, error);
} }
@ -100,7 +145,7 @@ export async function initApp(): Promise<void> {
// 初始化入口场景 // 初始化入口场景
const entryScene = "init"; const entryScene = "init";
if (sceneManager.hasScene(entryScene)) { if (sceneManager.hasScene(entryScene)) {
sceneManager.initScene(entryScene); await sceneManager.initScene(entryScene);
} else { } else {
logger.error(`initApp: entry scene "${entryScene}" not found`); logger.error(`initApp: entry scene "${entryScene}" not found`);
} }

39
src/scene/SceneManager.ts

@ -83,6 +83,14 @@ class SceneManager {
this.changeScene(name, options); this.changeScene(name, options);
}; };
// 兼容旧 PWindow:defineWindow 场景里仍使用 changeStage
(scene as IBaseScene & { changeStage?: typeof scene.changeScene }).changeStage = (
name: string,
options?: { isHolderLast?: boolean }
) => {
scene.changeScene(name, options);
};
if (scene.type === SceneType.Resident) { if (scene.type === SceneType.Resident) {
this.game.stage.addChild(scene.stage); this.game.stage.addChild(scene.stage);
logger.debug(`SceneManager: registered resident scene "${scene.name}"`); logger.debug(`SceneManager: registered resident scene "${scene.name}"`);
@ -91,8 +99,8 @@ class SceneManager {
} }
} }
/** 初始化入口场景 */ /** 初始化入口场景(与 changeScene 一致:加载资源 → 布局 → onLoad) */
initScene(name: string): void { async initScene(name: string): Promise<void> {
if (!name) { if (!name) {
logger.warn("SceneManager: scene name cannot be empty"); logger.warn("SceneManager: scene name cannot be empty");
return; return;
@ -103,9 +111,21 @@ class SceneManager {
if (scene.type === SceneType.Normal) { if (scene.type === SceneType.Normal) {
this.game.stage.addChild(scene.stage); this.game.stage.addChild(scene.stage);
} else if (scene.type === SceneType.Resident) {
scene.stage.visible = true;
}
if (!scene._assetsLoaded) {
await scene.loadBundle?.();
scene._assetsLoaded = true;
}
if (!scene._layoutDone) {
await scene.layout?.();
scene._layoutDone = true;
} }
await scene.onLoad?.();
this.emitStageChange(scene, undefined); await this.emitStageChange(scene, undefined);
logger.debug(`SceneManager: initialized scene "${name}"`); logger.debug(`SceneManager: initialized scene "${name}"`);
} }
@ -129,16 +149,19 @@ class SceneManager {
previous.stage.visible = false; previous.stage.visible = false;
previous.stage._isHolderLast = true; previous.stage._isHolderLast = true;
} else { } else {
// 销毁场景 // 销毁显示节点并卸载,但保留场景注册,便于之后再次 changeScene("welcome") 等
previous.stage.destroy({ children: true }); const oldStage = previous.stage;
this.game.stage.removeChild(previous.stage); oldStage.destroy({ children: true });
// 卸载资源 this.game.stage.removeChild(oldStage);
if (previous._assetsLoaded) { if (previous._assetsLoaded) {
await previous.unLoadBundle?.(); await previous.unLoadBundle?.();
previous._assetsLoaded = false; previous._assetsLoaded = false;
} }
await previous.onUnLoad?.(); await previous.onUnLoad?.();
this.scenes.delete(previous.name); const nextStage = new Container();
nextStage.label = previous.name;
(previous as { stage: Container }).stage = nextStage;
previous._layoutDone = false;
} }
} else if (previous.type === SceneType.Resident) { } else if (previous.type === SceneType.Resident) {
previous.stage.visible = false; previous.stage.visible = false;

75
src/stages/_global/page_0global.ts

@ -1,46 +1,45 @@
import { Button } from "@/components/Button"; import Button from "@/components/Button";
import { EP } from "@/enums"; import { SceneType } from "@/enums/SceneType";
import { defineWindow } from "@/Game"; import position from "@/utils/Position";
import Position from "@/Game/Position"; import { BaseScene } from "@/scene/BaseScene";
import { PWindow } from "@/Game/type"; import { Assets, Container, Texture } from "pixi.js";
import { Assets } from "pixi.js";
export default defineWindow( export default class Global extends BaseScene {
class extends PWindow { stage: Container = new Container();
btn: Button; btn?: Button;
async loadBundle() {
console.log(this.stage);
console.log("加载资源");
Assets.add({ alias: 'btn-bgaa', src: '/assets/images/button_square_depth_gloss.png' })
Assets.add({ alias: 'btn-bg-pressaa', src: '/assets/images/button_square_depth_gradient.png' })
const aa = await Assets.load('btn-bgaa')
console.log(aa);
constructor() {
super("0global", SceneType.Resident);
} }
async layout() {
this.stage.zIndex = 9999 async loadBundle(): Promise<void> {
this.btn = new Button({ Assets.add({ alias: "btn-bga", src: "/assets/images/button_square_depth_gloss.png" });
text: "全局", Assets.add({ alias: "btn-bg-press", src: "/assets/images/button_square_depth_gradient.png" });
bg: await Assets.load('btn-bgaa'), await Assets.load("btn-bga");
pressBg: await Assets.load("btn-bg-pressaa"), await Assets.load("btn-bg-press");
position: () => Position.get("center", "center", { y: 100, x: 0 }),
onClick: () => {
console.log("Button clicked");
} }
})
this.stage.addChild(this.btn.getView()) async layout(): Promise<void> {
this.stage.sortableChildren = true;
this.stage.zIndex = 9999;
// const btnBg = Assets.get<Texture>("btn-bga");
// const btnBgPress = Assets.get<Texture>("btn-bg-press");
// this.btn = new Button({
// text: "全局",
// bg: btnBg,
// pressBg: btnBgPress,
// position: () => position.get("center", "center", { y: 100, x: 0 }),
// onClick: () => {
// console.log("Button clicked");
// },
// });
// this.stage.addChild(this.btn.getView());
} }
onLoad() {
onLoad(): void {
console.log("global page loaded"); console.log("global page loaded");
} }
hide() {
// this.btn._comp.visible = false onUnLoad(): void {
}
show() {
// this.btn._comp.visible = true
}
onUnLoad() {
} }
}, }
EP.Resident
);

2
src/stages/page_init.ts

@ -9,7 +9,7 @@ import { loadAsset } from "@/Game/Assets";
export default defineWindow( export default defineWindow(
class extends PWindow { class extends PWindow {
stage: Container = null; stage: Container = new Container();
title: Text; // 标题 title: Text; // 标题
startBtn: Button; // 标题 startBtn: Button; // 标题
pixie: AnimatedSprite; // 标题 pixie: AnimatedSprite; // 标题

4
src/stages/welcome2/page_welcome2.ts

@ -1,8 +1,10 @@
import { defineWindow } from "@/Game"; import { defineWindow } from "@/Game";
import { Graphics } from "pixi.js"; import { Container, Graphics } from "pixi.js";
import { PWindow } from "@/Game/type"; import { PWindow } from "@/Game/type";
export default defineWindow("welcome2", class extends PWindow { export default defineWindow("welcome2", class extends PWindow {
stage: Container = new Container();
layout() { layout() {
const circle = new Graphics(); const circle = new Graphics();
circle.label = "circle"; circle.label = "circle";

Loading…
Cancel
Save