boardgame-phaser/packages/sample-game/src/main.tsx

97 lines
2.7 KiB
TypeScript
Raw Normal View History

2026-04-03 15:18:47 +08:00
import { h, render } from 'preact';
import { signal } from '@preact/signals-core';
2026-04-03 16:18:44 +08:00
import { useEffect, useState } from 'preact/hooks';
2026-04-03 15:18:47 +08:00
import Phaser from 'phaser';
import { createGameContext } 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';
2026-04-03 15:18:47 +08:00
import './style.css';
const gameContext = createGameContext<TicTacToeState>(registry, createInitialState);
const promptSignal = signal<null | Awaited<ReturnType<typeof gameContext.commands.promptQueue.pop>>>(null);
const commandLog = signal<Array<{ input: string; result: string; timestamp: number }>>([]);
2026-04-03 16:18:44 +08:00
// 监听 prompt 事件
2026-04-03 15:18:47 +08:00
gameContext.commands.on('prompt', (event) => {
promptSignal.value = event;
});
2026-04-03 16:18:44 +08:00
// 包装 run 方法以记录命令日志
2026-04-03 15:18:47 +08:00
const originalRun = gameContext.commands.run.bind(gameContext.commands);
(gameContext.commands as any).run = async (input: string) => {
const result = await originalRun(input);
commandLog.value = [
...commandLog.value,
{
input,
result: result.success ? `OK: ${JSON.stringify(result.result)}` : `ERR: ${result.error}`,
timestamp: Date.now(),
},
];
return result;
};
2026-04-03 16:18:44 +08:00
function App() {
const [phaserReady, setPhaserReady] = useState(false);
const [game, setGame] = useState<Phaser.Game | null>(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 传递 gameContext
phaserGame.scene.add('GameScene', GameScene, true, { gameContext });
2026-04-03 15:18:47 +08:00
2026-04-03 16:18:44 +08:00
setGame(phaserGame);
setPhaserReady(true);
2026-04-03 15:18:47 +08:00
2026-04-03 16:18:44 +08:00
return () => {
phaserGame.destroy(true);
};
}, []);
useEffect(() => {
if (phaserReady) {
gameContext.commands.run('setup');
}
}, [phaserReady]);
return (
<div className="flex flex-col h-screen">
<div className="flex-1 relative">
<div id="phaser-container" className="w-full h-full" />
<PromptDialog
prompt={promptSignal.value}
onSubmit={(input: string) => {
gameContext.commands._tryCommit(input);
promptSignal.value = null;
}}
onCancel={() => {
gameContext.commands._cancel('cancelled');
promptSignal.value = null;
}}
/>
</div>
<div className="p-4 bg-gray-100 border-t">
<CommandLog entries={commandLog} />
</div>
</div>
);
}
2026-04-03 15:18:47 +08:00
const ui = new GameUI({
container: document.getElementById('ui-root')!,
2026-04-03 16:18:44 +08:00
root: <App />,
2026-04-03 15:18:47 +08:00
});
ui.mount();