# boardgame-phaser 基于Phaser3/boardgame-core的游戏框架 ## 概述 项目使用pnpm monorepo管理,包含以下包: - `framework`:通用框架 - `boop-game`:boop样例 - `sample-game`:tic tac toe样例 游戏应当使用vite构建,基于`preact/signals`进行状态管理,使用`phaser3`实现游戏功能。 ## boardgame-core 项目使用`boardgame-core`进行游戏定义。 `boardgame-core`的内容可以在`framework/node_modules/boardgame-core`找到。 这个文件夹被.gitignore忽略,查看时需要绕开这一限制。 ## 编写GameModule 游戏逻辑以GameModule的形式定义: ```typescript import {createGameCommandRegistry, IGameContext} from "boardgame-core"; // 创建mutative游戏初始状态 export function createInitialState(): GameState { //... } // 运行游戏 export async function start(game: IGameContext) { // ... } // 可选 export const registry = createGameCommandRegistry(); ``` 使用以下步骤创建GameModule: ### 1. 定义状态 通常使用一个`regions: Record`和一个`parts: Record>`来记录桌游物件的摆放。 ```typescript import {Region} from "boardgame-core"; type GameState = { regions: { white: Region, black: Region, board: Region, }, pieces: Record>, currentPlayer: PlayerType, winner: WinnerType, }; ``` 游戏的部件可以从`csv`加载。详情见`boop-game/node_modules/inline-schema/`。 ``` /// parts.csv type, player, count string, string, number cat, white, 8 cat, black, 8 /// parts.csv.d.ts type PartsTable = { type: string; player: string; count: number; }[]; declare const data: PartsTable; export default data; ``` ### 2. 定义流程 使用`async function start(game: IGameContext)`作为入口。 使用`game.value`读取游戏当前状态 ```typescript async function gameEnd(game: IGameContext) { return game.value.winner; } ``` 需要修改状态时,使用`game.produce`或`game.produceAsync`。 ```typescript async function start(game: IGameContext) { await game.produceAsync(state => { state.currentPlayer = 'white'; }); } ``` 需要等待玩家交互时,使用`await game.prompt(promptDef, validator, player)`。 ```typescript import {createPromptDef} from "boardgame-core"; export const prompts = { play: createPromptDef<[PlayerType, number, number]>('play '), } async function turn(game: IGameContext, currentPlayer: PlayerType) { const {player, row, col} = await game.prompt( prompts.play, (player, row, col) => { if (player !== currentPlayer) throw `Wrong player!` return {player, row, col}; } ) } ``` ### 3. 编写测试 使用`vitest`编写测试,测试应当使用`GameHost`来模拟游戏环境。 ## 编写Phaser App 使用`framework/src/ui/PhaserBridge`来创建Phaser App。 使用`framework/src/scenes/GameHostScene`来创建游戏场景。 使用`GameHost`来控制游戏状态。 ```typescript export class GameHost, TResult=unknown> { // 获取游戏状态的只读快照 get state(): TState{} // 运行状态 readonly status: ReadonlySignal; // 运行中途需要玩家输入时使用 readonly activePromptSchema: ReadonlySignal; readonly activePromptPlayer: ReadonlySignal; // 玩家响应activePrompt的输入,若报错则返回string,否则返回null // promptDef应当从game module中导出 tryAnswerPrompt(promptDef: Promptdef,...args: TArgs): string | null {} // 添加中断,context.produceAsync会等待所有中断结束之后再继续 addInterruption(promise: Promise): void {} // 开始或者重新开始游戏 start(): Promise{} // 销毁 dispose(): void {} // 事件侦听 on(event: 'start' | 'dispose', listener: () => void): () => void {} } ```