docs(game-runtime): U1 约定正式化 runtime-api-2d.d.ts(rt 接口 TS 契约 · 第10类契约 additive)

plan 2026-06-18-001 U1 正式化产物(plan「1-2 轮后正式化」已满足:4 轮迭代+跨品类 9/9 实证+clickable 基元,约定已稳)。
手写 TS 契约(同 api.d.ts/game-host.d.ts 范式),与 gd-runtime.js 同形:
- gameDefinition 声明式模型:Render/Physics/Clickable/Marker 组件 + EntityDef/BehaviorDef(code)/RuleDef/SceneDef。
- rt 面(behavior 侧):实体 getEntity/entities/query/spawn/destroy + Entity 实例形 + input(轮询)/time(确定性,无 nowMs)/
  dt/random/score-win-lose(latch)/clamp-dist-overlap/fx/view。
- ForensicsState:固定取证键 + 逐实体 id 投影命名位(ball.x…)+ tag=target → targets[]。
- createRuntime(boot,gameDefinition)→Runtime(init/update/render/state/destroy/errors)签名。

落 game-runtime/src/host/(同 game-host.d.ts 之理:内部宿主↔behavior 接线面,非跨仓 SSOT);
additive 第 10 类契约,**不动现 8 契约**(故未改 source-project.schema.json;clickable 入其 kind 枚举=additive,
留 6c6g/创始人 决,contract-first 补正)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-18 06:38:50 -07:00
parent 80aaa116ab
commit 95b2572e65

View File

@ -0,0 +1,217 @@
// runtime-api-2d.d.ts —— 运行时访问约定2D 适配器 · behavior 侧 APITS 契约 · core-runtime-v0
// owner本 session引擎线 · plan 2026-06-18-001 U1 正式化产物) 消费方U2 build-from-source 装配器 + U3 GAMEDEF_SYSTEM prompt
//
// 【为什么手写 .d.ts、落 game-runtime/src/host/】
// 与 game-host.d.ts第 9 类契约)同理:发行版源 = ESM JS + JSDoc对外「类型契约」手写。本约定是 game-runtime
// 内部「宿主↔游戏 behavior」接线面behaviors 经 rt 操作世界),与 api.d.ts插件↔引擎面正交、与 game-host.d.ts
// (游戏工厂↔宿主面)相邻;故落 src/host/,不落 contracts/(避免跨仓 SSOT 反向 import。是否升格 contracts/ 由 6c6g 定。
//
// 【与实现同形】本文件与 gd-runtime.js 的 createRuntime/rt 必须同形同步;人/模型可读版见同目录 runtime-api-2d.md。
// 【状态】已过 4 轮迭代 + 跨品类 9/9 实证(含 clickable 基元),约定稳定,正式化为契约。后续演进只增不破已冻签名。
import type { PluginContext } from '../core/api.d.ts';
import type { GameHostBootContext } from './game-host.d.ts';
/*
* gameDefinition source-project.schema.json#/properties/gameDefinition
* */
/** 归一化颜色:'#rgb'/'#rrggbb' 或 {r,g,b,a}(0..1)。 */
export type Color = string | { r: number; g: number; b: number; a?: number };
/** 渲染组件声明式出图behaviors 只逻辑不画,渲染器据此画)。 */
export interface RenderComponent {
id?: string;
kind: 'render';
/** rect缺省以 transform 为中心)/ circle / fill铺满 390×844 视口作背景)。 */
shape?: 'rect' | 'circle' | 'fill';
color?: Color;
w?: number; h?: number; // rect
r?: number; // circle
}
/** 物理组件opt-in挂此组件的实体每帧 x+=vx*dt; y+=vy*dt有 gravity 则先 vy+=gravity*dt。 */
export interface PhysicsComponent {
id?: string;
kind: 'physics';
gravity?: number;
}
/**
* plan U3 occupied!==true
* occupied=true + spawn mark () + score + fx(/)
* (//), tap-handler tags:["target"] driver
*/
export interface ClickableComponent {
id?: string;
kind: 'clickable';
/** 命中加分(缺省 1。 */
score?: number;
/** 命中时 spawn 的标记实体的 render 形态(缺省黄色小圆)。 */
mark?: { shape?: 'rect' | 'circle'; color?: Color; r?: number; w?: number; h?: number };
/** 命中区半尺寸来源(缺省取 render 尺寸 / 80。 */
hitW?: number; hitH?: number;
}
/** 碰撞/自定义组件:声明式标记,由 behavior 自行 rt.overlap 判定 / 读取。 */
export interface MarkerComponent {
id?: string;
kind: 'collision' | 'custom';
[k: string]: unknown;
}
export type Component = RenderComponent | PhysicsComponent | ClickableComponent | MarkerComponent;
/** 实体声明schema entitytransform.position → 运行时 x/y可带 vx/vy/tags + 任意数值字段(idx/occupied 等)。 */
export interface EntityDef {
id: string;
transform: { position: { x: number; y: number; z?: number }; rotation?: number; scale?: number };
/** 组件引用components[].id 字符串或内联组件对象spawn 用)。 */
components?: Array<string | Component>;
vx?: number; vy?: number;
tags?: string[];
[k: string]: unknown; // 运行时可用的自定义数值/布尔字段idx/occupied/…)
}
/** 行为模块code 是一段 JS 逻辑串,运行期编译为 function(rt,self,dt)。init 跑一次,其余每帧。 */
export interface BehaviorDef {
id: string;
trigger: 'init' | 'update' | 'input' | 'collision' | 'timer';
/** 逻辑代码(规范字段 codejs 为兼容别名)。受校验边界静态扫描(禁 Math.random/Date/process/while(true)…)。 */
code?: string;
js?: string;
}
/** 规则condition 是无副作用 JS 布尔表达式rt/self 在作用域),为真按 outcome。 */
export interface RuleDef {
id: string;
condition: string;
outcome: 'win' | 'lose' | 'score' | 'advance';
}
/** 场景scenes[0].entityRefs 决定初始实例化哪些实体(缺省全量)。 */
export interface SceneDef {
id: string;
entityRefs: string[];
}
export interface GameDefinition {
entities: EntityDef[];
components?: Component[];
behaviors?: BehaviorDef[];
scenes?: SceneDef[];
rules?: RuleDef[];
}
/*
* rt behavior API new Function('rt','self','dt',code) rt
* / boot.ctx (, Math.random/Date.now) ctx.getEngine()( F )
* */
/** 运行时实体实例rt.getEntity/entities/query/spawn 返回)。 */
export interface Entity {
id: string;
x: number; y: number; vx: number; vy: number;
alive: boolean;
tags: Set<string>;
/** 已解析的组件对象数组。 */
components: Component[];
get(k: string): unknown;
set(k: string, v: unknown): unknown;
destroy(): void;
[k: string]: unknown; // behaviors 可直接读写自定义属性e.hp=3、e.occupied 等)
}
/** 受控输入轮询面(包 ctx.getInput() 受控事件为 behaviors 友好的轮询)。 */
export interface RtInput {
isDown(key: string): boolean;
justPressed(key: string): boolean;
justTapped(): boolean;
/** 当前指针(副本,写它不腐蚀内部态)。 */
readonly pointer: { x: number; y: number; down: boolean };
}
/** 受控时间面now=相对游戏时间秒,从 0不暴露墙钟 nowMs保确定性。 */
export interface RtTime {
now(): number;
}
export interface RtFx {
/** 经 ctx.getEngine().particles 真喷粒子(满 F 门;无引擎优雅 no-op。 */
burst(x: number, y: number, color?: Color): void;
/** 经 ctx.getEngine().audio.synth 真合成音效kind ∈ score/hit/lose/win/default。 */
beep(kind?: string): void;
}
/** behavior/rule 注入的运行时访问面(全部能力的唯一入口;其纯 API 面被 Object.freeze 防改写)。 */
export interface Rt {
// 实体
getEntity(id: string): Entity | null;
entities(): Entity[];
/** 按 tag 或组件 id/kind 查询活实体gameplay 主用 tag。 */
query(name: string): Entity[];
spawn(spec: Partial<EntityDef> & { x?: number; y?: number; vx?: number; vy?: number; tags?: string[]; components?: Array<string | Component> }): Entity;
destroy(e: Entity): void;
// 输入 / 时间 / 随机(确定性)
input: RtInput;
time: RtTime;
readonly dt: number;
random(): number;
randRange(a: number, b: number): number;
randInt(a: number, b: number): number;
// 分数胜负win/lose 置不可逆 latch 终态)
readonly score: number;
addScore(n?: number): number;
setScore(n: number): number;
win(): void;
lose(): void;
// 工具 / 特效 / 视口
clamp(v: number, lo: number, hi: number): number;
dist(ax: number, ay: number, bx: number, by: number): number;
/** AABB 重叠({x,y,w?,h?},缺省全尺寸 16显式 0 被尊重)。 */
overlap(a: { x: number; y: number; w?: number; h?: number }, b: { x: number; y: number; w?: number; h?: number }): boolean;
fx: RtFx;
view: { w: number; h: number };
}
/*
* _forensicsView().state() + driver
* */
/** state() 快照:固定取证键 + 逐实体按 id 投影命名位ball.x/paddle.x…+ tag='target' → targets[]。 */
export interface ForensicsState {
phase: 'booting' | 'playing' | 'gameover';
result: 'win' | 'lose' | null;
score: number;
elapsed: number;
remaining: number;
progress: number | null;
errors: string[];
entities: Array<{ id: string; x: number; y: number; tags: string[] }>;
/** tap-targets driver 读tag='target' 实体派生。 */
targets?: Array<{ x: number; y: number; idx: number; occupied: boolean; safe: boolean }>;
/** 逐实体 id 投影位(如 ball:{x,y,vx,vy,w?,h?,r?,angle?}),供 gatespec 命名路径解析。 */
[entityId: string]: unknown;
}
/*
* createRuntime + rtU2 init GameInstance init/update/render/state/destroy
* */
export interface Runtime {
rt: Rt;
init(): void;
update(dt: number): void;
render(g: CanvasRenderingContext2D): void;
state(): ForensicsState;
destroy(): void;
errors(): string[];
}
/**
* entities behaviors(new Function)/rules clickable + rt
* @param boot 宿 ctx:PluginContext
* @param gameDefinition
*/
export declare function createRuntime(boot: GameHostBootContext | { ctx: PluginContext; seed?: number; mainContext?: CanvasRenderingContext2D | null; canvas?: HTMLCanvasElement | null }, gameDefinition: GameDefinition): Runtime;
export default createRuntime;