import Phaser from 'phaser'; import { computed, signal, useSignal, useSignalEffect } from '@preact/signals'; import { createContext, h } from 'preact'; import { useContext } from 'preact/hooks'; import {ReadonlySignal} from "@preact/signals-core"; import type { ReactiveScene, ReactiveScenePhaserData } from '../scenes'; import { FadeScene as FadeSceneClass, FADE_SCENE_KEY } from '../scenes/FadeScene'; export interface SceneController { /** 启动场景(带淡入淡出过渡) */ launch(sceneKey: string, data?: Record): Promise; /** 当前活跃场景 key */ currentScene: ReadonlySignal; /** 是否正在过渡 */ isTransitioning: ReadonlySignal; } export interface PhaserGameContext { game: Phaser.Game; sceneController: SceneController; } export const phaserContext = createContext | null>(null); export const defaultPhaserConfig: Phaser.Types.Core.GameConfig = { type: Phaser.AUTO, width: 560, height: 560, parent: 'phaser-container', backgroundColor: '#f9fafb', scene: [], }; export interface PhaserGameProps { config?: Partial; children?: any; } export function PhaserGame(props: PhaserGameProps) { const gameSignal = useSignal({ game: undefined!, sceneController: undefined! }); const registeredScenes = useSignal>(new Map()); useSignalEffect(() => { const config: Phaser.Types.Core.GameConfig = { ...defaultPhaserConfig, ...props.config, }; const phaserGame = new Phaser.Game(config); // 添加 FadeScene const fadeScene = new FadeSceneClass(); phaserGame.scene.add(FADE_SCENE_KEY, fadeScene, false); // 创建 SceneController const currentScene = signal(null); const isTransitioning = signal(false); const sceneController: SceneController = { async launch(sceneKey: string, data?: Record) { if (isTransitioning.value) { console.warn('SceneController: 正在进行场景切换'); return; } isTransitioning.value = true; const fade = phaserGame.scene.getScene(FADE_SCENE_KEY) as FadeSceneClass; // 淡出到黑色 await fade.fadeOut(300); // 停止当前场景 if (currentScene.value) { phaserGame.scene.stop(currentScene.value); } // 启动新场景 phaserGame.scene.start(sceneKey, data); currentScene.value = sceneKey; // 淡入 await fade.fadeIn(300); isTransitioning.value = false; }, currentScene, isTransitioning, }; gameSignal.value = { game: phaserGame, sceneController }; return () => { gameSignal.value = { game: undefined!, sceneController: undefined! }; registeredScenes.value.clear(); phaserGame.destroy(true); }; }); return (
{props.children}
); } export interface PhaserSceneProps = {}> { sceneKey: string; scene: ReactiveScene; data?: TData; children?: any; } export const phaserSceneContext = createContext | null>(null); export function PhaserScene = {}>(props: PhaserSceneProps) { const phaserGameSignal = useContext(phaserContext); const sceneSignal = useSignal>(); useSignalEffect(() => { if (!phaserGameSignal) return; const ctx = phaserGameSignal.value; if (!ctx?.game) return; const game = ctx.game; 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; return () => { sceneSignal.value = undefined; // 不在这里移除场景,让 SceneController 管理生命周期 }; }); return }>{props.children}; }