docs(game-runtime): U1 运行时约定文本 runtime-api-2d.md(交 U3 prompt 嵌入)

plan 2026-06-18-001 U1 交付物「约定文字同步喂进 prompt → 交 U3 落 SaaPrompts 新 SYSTEM」。

新增 game-runtime/src/host/runtime-api-2d.md——写 gameDefinition behavior 逻辑时能用的全部 rt 面
的人/模型可读契约:gameDefinition 结构 / behavior 代码契约(new Function('rt','self','dt',code)+
trigger 语义+四条硬约束)/ rules / rt 面全 API 参考 / 组件 / 4 原型 few-shot / 常见坑(治真实模型
漂移:禁约定外 helper、禁 Math.random、终态置 latch、behavior 不画图、关键事件挂 fx)。

U3 的 GAMEDEF_SYSTEM prompt 直接嵌入本文。状态 v0(已过 4 类原型表达力证),保留补 idiom 余量。
落点同 game-host.d.ts 之理(内部宿主↔behavior 接线面 → game-runtime/src/host/);
是否升格 contracts/ 跨仓契约由 6c6g(contracts owner)定。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-17 23:11:14 -07:00
parent 4292f4e793
commit c8526f1dd6

View File

@ -0,0 +1,120 @@
# 运行时访问约定(2D 适配器 · behavior 侧 API)—— runtime-api-2d v0
> plan 2026-06-18-001 U1 交付物。本文件是「写 gameDefinition 的 behavior 逻辑时能用的全部 `rt` 面」的**人/模型可读契约**,
> U3 的 `GAMEDEF_SYSTEM` prompt 直接嵌入本文(+ 末尾 few-shot)。实现见 `gd-runtime.js`,类型雏形随后正式化为 `runtime-api-2d.d.ts`。
>
> **状态 v0(plan「别冻早」)**:已过 4 类原型表达力证(见 `gd-archetypes.test.mjs`),接口稳定但保留随真实模型输出补 idiom 的余量。
> **落点说明**:与 `game-host.d.ts`(第 9 类契约)同理,本约定是 game-runtime 内部「宿主↔游戏 behavior」接线面,故落 `game-runtime/src/host/`;
> 是否升格为 `contracts/` 跨仓契约由 6c6g(大脑线/contracts owner)定。
---
## 1. 游戏 = 声明式 gameDefinition(改源不改打包产物)
一个游戏 = 一份 `gameDefinition` JSON(结构见 `contracts/agent-loop/source-project.schema.json`):
| 字段 | 含义 |
|---|---|
| `entities[]` | 实体:`{id, transform:{position:{x,y}}, components:[组件id...]}`(可带 `vx/vy/tags` 供运行时用) |
| `components[]` | 组件定义:`{id, kind, ...}`,`kind ∈ render/collision/physics/custom` |
| `behaviors[]` | 行为模块:`{id, trigger, code}`——`code` 是一段 **JS 逻辑字符串**,运行期编译执行 |
| `scenes[]` | 场景:`{id, entityRefs:[实体id...]}`(v0 取 `scenes[0]` 决定初始实例化哪些实体;缺省全量) |
| `rules[]` | 胜负规则:`{id, condition, outcome}`——`condition` 是 JS 布尔表达式串,`outcome ∈ win/lose/score/advance` |
**核心范式**:实体/组件/场景/规则是**声明式数据**;唯一的「逻辑代码」是 `behaviors[].code` 和 `rules[].condition`,
它们经受控的 `rt` 面操作世界。**behaviors 只写逻辑、不写画**(出图由声明式渲染器据 render 组件自动完成)。
---
## 2. behavior 代码契约
每个 behavior 的 `code` 被编译为 `new Function('rt', 'self', 'dt', code)`,每次调用注入三参:
- **`rt`** — 运行时访问面(见 §4),操作世界的唯一入口。
- **`self`** — 本 behavior 的**持久局部态**(普通对象 `{}`,跨帧保留)。存计时器/累加器:`self.t = (self.t||0) + dt;`
- **`dt`** — 本帧时间步(秒)。
**trigger 语义**:
- `init`:世界建好后**跑一次**(布初始实体/初值)。
- `update` / `input` / `collision` / `timer`:**每帧跑一次**(输入经 `rt.input` 轮询;碰撞/计时在 behavior 内自查)。
**硬约束(违反 = 生成缺陷)**:
1. **确定性**:随机一律 `rt.random/rt.randRange/rt.randInt`,时间一律 `rt.time.now()`/`dt`——**禁 `Math.random` / `Date.now` / `performance.now`**。
2. **只用 `rt` 面**:禁自造约定外 helper(如 `rt._spawnFood`)、禁 `document`/`window`/裸引擎/`requestAnimationFrame`/`addEventListener`。要生成实体用 `rt.spawn`。
3. **终态置 latch**:胜负用 `rt.win()`/`rt.lose()`(置不可逆终态),**不要**用计分或自定义 flag 表达「游戏结束」。
4. **特效经 `rt.fx`**:碰撞/得分等关键事件调 `rt.fx.burst(...)` / `rt.fx.beep(...)`(真接引擎粒子/音频)。
---
## 3. 规则(rules)
`condition` 是 JS 布尔表达式串,作用域内有 `rt`(与 `self`)。每帧求值;为真时按 `outcome`:
- `win` / `lose` → 置 latch 终态(一次性,不可逆)。
- `score` → 上升沿 +1 分(防每帧重复加)。
- `advance` → 场景推进(v0 占位)。
例:`{ "id":"win", "condition":"rt.score >= 10", "outcome":"win" }`
---
## 4. `rt` 面参考(全部可用 API)
### 实体
- `rt.getEntity(id)` → 实体或 null(仅活实体)
- `rt.entities()` → 全部活实体数组
- `rt.query(name)` → 按 tag 或组件 id/kind 过滤的活实体数组
- `rt.spawn({x, y, vx?, vy?, tags?, components?})` → 新实体(`components` 可内联组件对象);返回实体引用
- `rt.destroy(entity)` → 标记死亡(帧末回收)
- 实体字段:`.x .y .vx .vy .alive .tags(Set) .components(数组)`;方法 `.get(k)` / `.set(k,v)` / `.destroy()`;可直接读写任意属性(`e.hp = 3`)
### 输入(轮询)
- `rt.input.isDown(key)` → 是否按住(如 `'ArrowLeft'`/`'Space'`)
- `rt.input.justPressed(key)` → 本帧是否刚按下
- `rt.input.justTapped()` → 本帧是否发生指针按下
- `rt.input.pointer` → `{x, y, down}`
### 时间 / 随机(确定性)
- `rt.time.now()` → 相对游戏时间(秒,从 0 累加;**用这个**,非绝对钟)
- `rt.dt` → 本帧步长(秒,= 注入的 `dt`)
- `rt.random()` → `[0,1)`;`rt.randRange(a,b)` → `[a,b)`;`rt.randInt(a,b)` → `[a,b]` 整数
### 分数 / 胜负
- `rt.score`(读)/ `rt.addScore(n=1)` / `rt.setScore(n)`
- `rt.win()` / `rt.lose()` → 置 latch 终态
### 工具 / 特效
- `rt.clamp(v,lo,hi)` / `rt.dist(ax,ay,bx,by)` / `rt.overlap(a,b)`(AABB,`a/b={x,y,w?,h?}`,缺省半尺寸 16)
- `rt.fx.burst(x,y,color)` → 引擎粒子(color 接 `'#rrggbb'` 或 `{r,g,b,a}`)
- `rt.fx.beep(kind)` → 引擎音效(kind ∈ `score/hit/lose/win/default`)
- `rt.view` → `{w:390, h:844}`(视口;边界判断用)
---
## 5. 组件(components)
- **render**:`{id, kind:'render', shape, color, ...}`
- `shape:'rect'` + `w,h`(以实体 transform 为中心)
- `shape:'circle'` + `r`
- `shape:'fill'` + `color`(铺满视口,作背景)
- **physics**:`{id, kind:'physics', gravity?}`——挂此组件的实体每帧自动 `x+=vx*dt; y+=vy*dt`(有 `gravity` 则先 `vy+=gravity*dt`)。不挂则位置全由 behavior 控制。
- **collision / custom**:声明式标记,由 behavior 自行 `rt.overlap` 判定 / 读取。
---
## 6. 原型范式(few-shot · 见 `gd-archetypes.test.mjs` 完整可跑版)
- **paddle-intercept(打砖块/pong)**:板 entity + 球 entity(`vx/vy`);update behavior 里键/指针控板、球积分+墙反弹、`rt.overlap` 板拦截 `rt.addScore`+`rt.fx.burst`、落底 `rt.lose()`。
- **event-clicker(点击器/放置)**:按钮 entity;input behavior 里 `if(rt.input.justTapped()) rt.addScore(1)`;rule `rt.score>=N → win`。
- **dodge-spawn(躲避)**:玩家 entity;update behavior 里键控玩家、定时 `rt.spawn` 随机位敌人(`rt.randRange`)、遍历 `rt.query('enemy')` 下落+出界 `rt.destroy`+`rt.overlap` 撞玩家 `rt.lose()`。
- **runner(跑酷)**:玩家挂 `physics{gravity}`;update behavior 里 `rt.input.justTapped()` 起跳(置 `vy`)、落地复位、定时 `rt.spawn` 障碍(`vx`)、`rt.overlap` 撞障碍 `rt.lose()` / 出界 `rt.addScore`。
---
## 7. 常见坑(治真实模型漂移)
- ❌ 自造 `rt._spawnFood` 等约定外 helper → ✅ 用 `rt.spawn(...)`。
- ❌ `Math.random()` / `Date.now()` → ✅ `rt.random()` / `rt.time.now()`(确定性,否则取证不可复现 + 静态门拒)。
- ❌ 用分数/flag 表达「结束」 → ✅ `rt.win()`/`rt.lose()` 置 latch。
- ❌ 在 behavior 里画图(`ctx.fillRect`)→ ✅ 给实体加 render 组件,渲染器自动出图。
- ❌ 关键事件无特效 → ✅ 碰撞/得分调 `rt.fx.burst`/`rt.fx.beep`(满「真接线」门)。