boardgame-core/docs/game-host.md

166 lines
4.0 KiB
Markdown
Raw Normal View History

2026-04-04 13:20:34 +08:00
# 使用 GameHost
`GameHost` 是游戏运行的核心容器,负责管理游戏状态、命令执行和玩家交互的生命周期。
## 创建 GameHost
通过 `createGameHost` 传入一个 GameModule 来创建:
```ts
import { createGameHost } from 'boardgame-core';
import * as tictactoe from 'boardgame-core/samples/tic-tac-toe';
const host = createGameHost(tictactoe);
```
## 响应式状态
GameHost 暴露的所有属性都是响应式 Signal可以直接用于 UI 渲染或 `effect()`
```ts
import { effect } from '@preact/signals-core';
// 游戏状态
effect(() => {
2026-04-04 22:42:23 +08:00
console.log(host.context.value.currentPlayer);
console.log(host.context.value.winner);
2026-04-04 13:20:34 +08:00
});
// 生命周期状态: 'created' | 'running' | 'disposed'
effect(() => {
console.log('Status:', host.status.value);
});
// 当前等待的玩家输入 schema
effect(() => {
const schema = host.activePromptSchema.value;
if (schema) {
console.log('Waiting for:', schema.name, schema.params);
}
});
// 当前等待的玩家
effect(() => {
console.log('Current player prompt:', host.activePromptPlayer.value);
});
```
## 启动游戏
调用 `setup()` 并传入初始化命令名来启动游戏:
```ts
await host.setup('setup');
```
2026-04-04 22:42:23 +08:00
这会重置游戏状态、取消当前活动提示、在后台启动指定的 setup 命令(不等待完成),并将状态设为 `'running'`
2026-04-04 13:20:34 +08:00
## 处理玩家输入
当命令通过 `this.prompt()` 等待玩家输入时,使用 `onInput()` 提交输入:
```ts
// 提交玩家操作,返回错误信息或 null
const error = host.onInput('play X 1 2');
if (error) {
console.log('输入无效:', error);
// 玩家可以重新输入
} else {
// 输入已被接受,命令继续执行
}
```
## 监听事件
```ts
// 监听游戏设置完成
host.on('setup', () => {
console.log('Game initialized');
});
// 监听游戏销毁
host.on('dispose', () => {
console.log('Game disposed');
});
// on() 返回取消订阅函数
const unsubscribe = host.on('setup', handler);
unsubscribe(); // 取消监听
```
## 重新开始游戏
```ts
// 取消当前命令,重置状态,重新运行 setup 命令
await host.setup('setup');
```
## 销毁游戏
```ts
host.dispose();
```
销毁后会取消所有活动命令、清理事件监听器,并将状态设为 `'disposed'`。销毁后无法再次使用。
## 完整示例
```ts
import { effect } from '@preact/signals-core';
import { createGameHost } from 'boardgame-core';
import * as tictactoe from 'boardgame-core/samples/tic-tac-toe';
const host = createGameHost(tictactoe);
// 监听状态变化
effect(() => {
2026-04-04 22:42:23 +08:00
const state = host.context.value;
2026-04-04 13:20:34 +08:00
console.log(`${state.currentPlayer}'s turn (turn ${state.turn + 1})`);
if (state.winner) {
console.log('Winner:', state.winner);
}
});
// 启动游戏
await host.setup('setup');
// 游戏循环:等待提示 → 提交输入
2026-04-04 22:42:23 +08:00
// 注意setup() 会立即返回,但 prompt 可能需要一些时间才能激活
// 实际应用中应该等待 activePromptSchema 变为非 null
2026-04-04 13:20:34 +08:00
while (host.status.value === 'running' && host.activePromptSchema.value) {
const schema = host.activePromptSchema.value!;
console.log('Waiting for input:', schema.name);
// 这里可以从 UI/网络等获取输入
const input = await getPlayerInput();
const error = host.onInput(input);
if (error) {
console.log('Invalid:', error);
}
}
// 游戏结束后可以重新开始
// await host.setup('setup');
// 或彻底销毁
// host.dispose();
```
2026-04-04 15:27:37 +08:00
## 动画同步
如需在状态更新之间播放动画,参考 [动画与状态更新同步](./animation-sync.md)。
`GameHost` 提供了两个方法:
| 方法 | 说明 |
|---|---|
| `addInterruption(promise: Promise<void>)` | 注册动画中断,下一个 `produceAsync` 会等待它 |
| `clearInterruptions()` | 清除所有未完成的中断 |
```ts
// UI 层:检测到状态变化后播放动画并注册中断
host.addInterruption(playAnimation('place', data));
```