import { h, render } from 'preact'; import { signal } from '@preact/signals-core'; import { useEffect, useState, useCallback } from 'preact/hooks'; import Phaser from 'phaser'; import { createGameHost } from 'boardgame-core'; import { GameUI, PromptDialog, CommandLog } from 'boardgame-phaser'; import { createInitialState, registry, type TicTacToeState } from './game/tic-tac-toe'; import { GameScene } from './scenes/GameScene'; import './style.css'; // 创建 GameHost 实例,自动管理状态和 prompt const gameHost = createGameHost( { registry, createInitialState }, 'setup', { autoStart: false } ); const commandLog = signal>([]); // 记录命令日志的辅助函数 function logCommand(input: string, result: { success: boolean; result?: unknown; error?: string }) { commandLog.value = [ ...commandLog.value, { input, result: result.success ? `OK: ${JSON.stringify(result.result)}` : `ERR: ${result.error}`, timestamp: Date.now(), }, ]; } function App() { const [phaserReady, setPhaserReady] = useState(false); const [game, setGame] = useState(null); const [scene, setScene] = useState(null); const [gameState, setGameState] = useState(null); const [promptSchema, setPromptSchema] = useState(null); useEffect(() => { const phaserConfig: Phaser.Types.Core.GameConfig = { type: Phaser.AUTO, width: 560, height: 560, parent: 'phaser-container', backgroundColor: '#f9fafb', scene: [], }; const phaserGame = new Phaser.Game(phaserConfig); // 通过 init 传递 gameHost const gameScene = new GameScene(); phaserGame.scene.add('GameScene', gameScene, true, { gameHost }); setGame(phaserGame); setScene(gameScene); setPhaserReady(true); return () => { gameHost.dispose(); phaserGame.destroy(true); }; }, []); useEffect(() => { if (phaserReady && scene) { // 监听 prompt 状态变化 const disposePromptSchema = gameHost.activePromptSchema.subscribe((schema) => { setPromptSchema(schema); scene.promptSchema.current = schema; }); // 监听状态变化 const disposeState = gameHost.state.subscribe(() => { setGameState(gameHost.state.value as TicTacToeState); }); // 运行游戏设置 gameHost.setup('setup').then(() => { logCommand('setup', { success: true }); }).catch(err => { logCommand('setup', { success: false, error: err.message }); }); return () => { disposePromptSchema(); disposeState(); }; } }, [phaserReady, scene]); const handlePromptSubmit = useCallback((input: string) => { const error = gameHost.onInput(input); if (error === null) { logCommand(input, { success: true }); setPromptSchema(null); if (scene) { scene.promptSchema.current = null; } } else { logCommand(input, { success: false, error }); } }, []); const handlePromptCancel = useCallback(() => { gameHost.commands._cancel('User cancelled'); setPromptSchema(null); if (scene) { scene.promptSchema.current = null; } }, []); const handleReset = useCallback(() => { gameHost.commands.run('reset').then(result => { logCommand('reset', result); }); }, []); return (
{/* 游戏状态显示 */} {gameState && !gameState.winner && (
{gameState.currentPlayer}'s Turn
)} {gameState?.winner && (
{gameState.winner === 'draw' ? "It's a Draw!" : `${gameState.winner} Wins!`}
)} null, cancel: () => {} } : null} onSubmit={handlePromptSubmit} onCancel={handlePromptCancel} />
Command Log
); } const ui = new GameUI({ container: document.getElementById('ui-root')!, root: , }); ui.mount();