166 lines
4.0 KiB
Markdown
166 lines
4.0 KiB
Markdown
# 使用 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(() => {
|
||
console.log(host.context.value.currentPlayer);
|
||
console.log(host.context.value.winner);
|
||
});
|
||
|
||
// 生命周期状态: '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');
|
||
```
|
||
|
||
这会重置游戏状态、取消当前活动提示、在后台启动指定的 setup 命令(不等待完成),并将状态设为 `'running'`。
|
||
|
||
## 处理玩家输入
|
||
|
||
当命令通过 `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(() => {
|
||
const state = host.context.value;
|
||
console.log(`${state.currentPlayer}'s turn (turn ${state.turn + 1})`);
|
||
if (state.winner) {
|
||
console.log('Winner:', state.winner);
|
||
}
|
||
});
|
||
|
||
// 启动游戏
|
||
await host.setup('setup');
|
||
|
||
// 游戏循环:等待提示 → 提交输入
|
||
// 注意:setup() 会立即返回,但 prompt 可能需要一些时间才能激活
|
||
// 实际应用中应该等待 activePromptSchema 变为非 null
|
||
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();
|
||
```
|
||
|
||
## 动画同步
|
||
|
||
如需在状态更新之间播放动画,参考 [动画与状态更新同步](./animation-sync.md)。
|
||
|
||
`GameHost` 提供了两个方法:
|
||
|
||
| 方法 | 说明 |
|
||
|---|---|
|
||
| `addInterruption(promise: Promise<void>)` | 注册动画中断,下一个 `produceAsync` 会等待它 |
|
||
| `clearInterruptions()` | 清除所有未完成的中断 |
|
||
|
||
```ts
|
||
// UI 层:检测到状态变化后播放动画并注册中断
|
||
host.addInterruption(playAnimation('place', data));
|
||
```
|