refactor: improved scene manager
This commit is contained in:
parent
fe57583a8f
commit
9ab7ae3e60
|
|
@ -10,7 +10,14 @@ export abstract class GameHostScene<TState extends Record<string, unknown>>
|
|||
extends ReactiveScene<GameHostSceneOptions<TState>>
|
||||
{
|
||||
public get gameHost(): GameHost<TState> {
|
||||
return this.initData.gameHost as GameHost<TState>;
|
||||
const gameHost = this.initData.gameHost as GameHost<TState>;
|
||||
if (!gameHost) {
|
||||
throw new Error(
|
||||
`GameHostScene (${this.scene.key}): gameHost 未提供。` +
|
||||
`确保在 PhaserScene 组件的 data 属性中传入 gameHost。`
|
||||
);
|
||||
}
|
||||
return gameHost;
|
||||
}
|
||||
|
||||
public get state(): TState {
|
||||
|
|
|
|||
|
|
@ -29,13 +29,19 @@ export abstract class ReactiveScene<TData extends Record<string, unknown> = {}>
|
|||
implements IDisposable
|
||||
{
|
||||
protected disposables = new DisposableBag();
|
||||
private _initData!: TData & ReactiveScenePhaserData;
|
||||
private _initData?: TData & ReactiveScenePhaserData;
|
||||
|
||||
/**
|
||||
* 获取通过 init() 注入的数据
|
||||
* 在 create() 阶段保证可用
|
||||
*/
|
||||
public get initData(): TData & ReactiveScenePhaserData {
|
||||
if (!this._initData) {
|
||||
throw new Error(
|
||||
`ReactiveScene (${this.scene.key}): initData 尚未初始化。` +
|
||||
`确保场景通过 PhaserScene 组件注册,并在 create() 阶段访问。`
|
||||
);
|
||||
}
|
||||
return this._initData;
|
||||
}
|
||||
|
||||
|
|
@ -43,14 +49,14 @@ export abstract class ReactiveScene<TData extends Record<string, unknown> = {}>
|
|||
* 获取 Phaser game 实例的响应式信号
|
||||
*/
|
||||
public get phaserGame(): ReadonlySignal<{ game: Phaser.Game }> {
|
||||
return this._initData.phaserGame;
|
||||
return this.initData.phaserGame;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景控制器
|
||||
*/
|
||||
public get sceneController(): SceneController {
|
||||
return this._initData.sceneController;
|
||||
return this.initData.sceneController;
|
||||
}
|
||||
|
||||
constructor(key?: string) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import Phaser from 'phaser';
|
||||
import { signal, useSignal, useSignalEffect } from '@preact/signals';
|
||||
import { createContext, h } from 'preact';
|
||||
import { useContext } from 'preact/hooks';
|
||||
import {ReadonlySignal} from "@preact/signals-core";
|
||||
import { useContext, useEffect, useRef } from 'preact/hooks';
|
||||
import { ReadonlySignal } from "@preact/signals-core";
|
||||
import type { ReactiveScene } from '../scenes';
|
||||
import { FadeScene as FadeSceneClass, FADE_SCENE_KEY } from '../scenes/FadeScene';
|
||||
|
||||
|
|
@ -38,9 +38,17 @@ export interface PhaserGameProps {
|
|||
children?: any;
|
||||
}
|
||||
|
||||
/** 存储待注册的场景配置 */
|
||||
interface SceneRegistration<TData extends Record<string, unknown> = {}> {
|
||||
sceneKey: string;
|
||||
scene: ReactiveScene<TData>;
|
||||
initData?: TData;
|
||||
}
|
||||
|
||||
export function PhaserGame(props: PhaserGameProps) {
|
||||
const gameSignal = useSignal<PhaserGameContext>({ game: undefined!, sceneController: undefined! });
|
||||
const initialSceneLaunched = useSignal(false);
|
||||
const scenesRef = useRef<Map<string, SceneRegistration>>(new Map());
|
||||
const initialSceneLaunched = useRef(false);
|
||||
|
||||
useSignalEffect(() => {
|
||||
const config: Phaser.Types.Core.GameConfig = {
|
||||
|
|
@ -64,6 +72,12 @@ export function PhaserGame(props: PhaserGameProps) {
|
|||
return;
|
||||
}
|
||||
|
||||
// 验证场景是否已注册
|
||||
if (!phaserGame.scene.getScene(sceneKey) && !scenesRef.current.has(sceneKey)) {
|
||||
console.error(`SceneController: 场景 "${sceneKey}" 未注册`);
|
||||
return;
|
||||
}
|
||||
|
||||
isTransitioning.value = true;
|
||||
const fade = phaserGame.scene.getScene(FADE_SCENE_KEY) as FadeSceneClass;
|
||||
|
||||
|
|
@ -75,6 +89,23 @@ export function PhaserGame(props: PhaserGameProps) {
|
|||
phaserGame.scene.stop(currentScene.value);
|
||||
}
|
||||
|
||||
// 确保场景已注册后再启动
|
||||
if (!phaserGame.scene.getScene(sceneKey)) {
|
||||
const registration = scenesRef.current.get(sceneKey);
|
||||
if (registration) {
|
||||
phaserGame.scene.add(
|
||||
sceneKey,
|
||||
registration.scene,
|
||||
false,
|
||||
{
|
||||
...registration.initData,
|
||||
phaserGame: gameSignal,
|
||||
sceneController,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 启动新场景
|
||||
phaserGame.scene.start(sceneKey);
|
||||
currentScene.value = sceneKey;
|
||||
|
|
@ -91,19 +122,23 @@ export function PhaserGame(props: PhaserGameProps) {
|
|||
|
||||
return () => {
|
||||
gameSignal.value = { game: undefined!, sceneController: undefined! };
|
||||
initialSceneLaunched.value = false;
|
||||
scenesRef.current.clear();
|
||||
initialSceneLaunched.current = false;
|
||||
phaserGame.destroy(true);
|
||||
};
|
||||
});
|
||||
|
||||
// 启动初始场景
|
||||
useSignalEffect(() => {
|
||||
// 启动初始场景(仅一次)
|
||||
useEffect(() => {
|
||||
const ctx = gameSignal.value;
|
||||
if (!initialSceneLaunched.value && props.initialScene && ctx?.sceneController) {
|
||||
initialSceneLaunched.value = true;
|
||||
ctx.sceneController.launch(props.initialScene);
|
||||
}
|
||||
if (!initialSceneLaunched.current && props.initialScene && ctx?.sceneController) {
|
||||
initialSceneLaunched.current = true;
|
||||
// 使用 microtask 确保所有子组件的场景注册已完成
|
||||
Promise.resolve().then(() => {
|
||||
ctx.sceneController.launch(props.initialScene!);
|
||||
});
|
||||
}
|
||||
}, [gameSignal.value, props.initialScene]);
|
||||
|
||||
return (
|
||||
<div id="phaser-container" className="w-full h-full">
|
||||
|
|
@ -122,9 +157,11 @@ export interface PhaserSceneProps<TData extends Record<string, unknown> = {}> {
|
|||
}
|
||||
|
||||
export const phaserSceneContext = createContext<ReadonlySignal<ReactiveScene> | null>(null);
|
||||
|
||||
export function PhaserScene<TData extends Record<string, unknown> = {}>(props: PhaserSceneProps<TData>) {
|
||||
const phaserGameSignal = useContext(phaserContext);
|
||||
const sceneSignal = useSignal<ReactiveScene<TData>>();
|
||||
const registered = useRef(false);
|
||||
|
||||
useSignalEffect(() => {
|
||||
if (!phaserGameSignal) return;
|
||||
|
|
@ -132,20 +169,23 @@ export function PhaserScene<TData extends Record<string, unknown> = {}>(props: P
|
|||
if (!ctx?.game) return;
|
||||
|
||||
const game = ctx.game;
|
||||
|
||||
// 注册场景到 Phaser(但不启动)
|
||||
if (!game.scene.getScene(props.sceneKey)) {
|
||||
const initData = {
|
||||
...props.data,
|
||||
phaserGame: phaserGameSignal,
|
||||
sceneController: ctx.sceneController,
|
||||
};
|
||||
|
||||
// 注册场景但不启动
|
||||
if (!game.scene.getScene(props.sceneKey)) {
|
||||
game.scene.add(props.sceneKey, props.scene, false, initData);
|
||||
}
|
||||
|
||||
sceneSignal.value = props.scene;
|
||||
registered.current = true;
|
||||
|
||||
return () => {
|
||||
sceneSignal.value = undefined;
|
||||
registered.current = false;
|
||||
// 不在这里移除场景,让 SceneController 管理生命周期
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue