Per user request: split the development process into per-topic docs under docs/dev-log/. Each entry follows the same structure: What was asked → Problems → How solved Files: - README.md index - 01-initial-scope-and-design.md - 02-ui-primitives-api-design.md - 03-monorepo-scaffold.md - 04-ui-primitive-impl.md - 05-drag-and-drop.md - 06-three-games.md - 07-bugfixes-round-1.md - lessons-learned.md all gotchas consolidated - decisions.md locked decisions with dates Total: 1071 lines across 10 files.
82 lines
2.7 KiB
Markdown
82 lines
2.7 KiB
Markdown
# 02 · UI 原语 API 设计
|
||
|
||
## 做什么
|
||
|
||
在动手写代码之前,先把 UI 组件的 props、game.ui 字段结构、数据流定义清楚,写成 `docs/ui-primitives.md` 供实现参考。
|
||
|
||
## 遇到什么问题
|
||
|
||
1. **接口界定**:组件要通用(支持任意 boardgame.io 游戏),又不能过于抽象失去表现力。
|
||
2. **game.ui 字段怎么设计**:是松散的"约定"还是强类型的 schema?
|
||
3. **拖拽 API**:TTS 用 Lua 控制每个对象的交互。JS 端怎么抽象?
|
||
4. **玩家视角**:boardgame.io master 会按 playerID 过滤状态,UI 怎么配合?
|
||
|
||
## 怎么解决
|
||
|
||
### 5 个设计原则
|
||
|
||
1. **声明式**:业务代码只描述"是什么",不描述"怎么画"
|
||
2. **数据驱动**:所有可见状态都来自 `G`(boardgame.io 的 state)
|
||
3. **服务器权威**:所有 game-affecting 操作都通过 `onMove` 触发 reducer
|
||
4. **玩家视角**:UI 接收的 `G` 已经是 boardgame.io 过滤后的版本
|
||
5. **可扩展**:通过 `game.ui.overrides` 注入自定义组件
|
||
|
||
### 8 个组件
|
||
|
||
`<Board>` `<Zone>` `<Hand>` `<Deck>` `<Card>` `<Token>` `<ActionButton>` `<Dice>`
|
||
|
||
### 6 个 zone type
|
||
|
||
`hand` / `pile` / `discard` / `grid` / `area` / `free`
|
||
|
||
### 关键 schema
|
||
|
||
```ts
|
||
interface UISchema {
|
||
background?: string;
|
||
zones: ZoneDef[];
|
||
cards?: CardTemplate;
|
||
tokens?: TokenDef[];
|
||
actions?: ActionDef[];
|
||
overrides?: UIOverrides;
|
||
}
|
||
|
||
interface ZoneDef {
|
||
id: string;
|
||
type: 'hand' | 'pile' | 'discard' | 'grid' | 'area' | 'free';
|
||
position: { x: number; y: number };
|
||
size?: { w: number; h: number };
|
||
layout?: { fan?: number; spacing?: number; maxWidth?: number };
|
||
faceDown?: boolean;
|
||
owner?: string | null; // 'self' | 'P0' | null(公开)
|
||
collection?: string; // G 中持有 ID 数组的字段名
|
||
entityField?: string; // G 中持有 ID → 实体映射的字段名
|
||
dropMove?: string; // 接收 drop 时调用的 move
|
||
dropArgs?: (cardIds, fromZoneId, toZoneId) => any[];
|
||
accepts?: string | ((card, ctx) => boolean); // 接收规则
|
||
}
|
||
```
|
||
|
||
### 6 个待决项(已锁定 ✅)
|
||
|
||
| 待决项 | 决定 |
|
||
| --- | --- |
|
||
| zone 坐标系统 | 固定像素 + Board 视口缩放 |
|
||
| 动画策略 | CSS transition 默认 + 游戏可覆盖 |
|
||
| 可访问性 | v1 仅鼠标 |
|
||
| 触屏/移动端 | v1 仅桌面 |
|
||
| 多选 UI | v1 仅 Shift+click |
|
||
| 牌堆数量显示 | 角标显示数量 |
|
||
|
||
资产格式:SVG 内嵌字符串优先(fallback DataURL/URL)。
|
||
|
||
## 成果
|
||
|
||
- `docs/ui-primitives.md`(15 KB,511 行)
|
||
- 6 个待决项的设计默认值锁定,避免实现时反复讨论
|
||
|
||
## 关联
|
||
|
||
- [04-ui-primitive-impl.md](./04-ui-primitive-impl.md) — 按这份设计实现
|
||
- [03-monorepo-scaffold.md](./03-monorepo-scaffold.md) — 同步开始搭骨架
|