根治 B(缩 M3 错面,结构性根除"点击在错 phase 消费/永不消费→启动不了"): - input 订阅收归 L1(_template/src/game.js wrapper):L3 只写实例方法 handleTap(x,y)/handleKey(key), L1 在 pointerdown/keydown 直达派发;_template/src/game-logic.js 范例改用 handleTap。 - tools.mjs check:拦 ctx.getInput + 必须暴露 ≥1 输入方法;prompt.mjs/README/skill 同步输入契约。 - 机制+门双实证(node 全链路:menu→点开始→play、点客加分、点空白不加分;check 对 getInput/零输入方法拦截)。 driver(play.cdp.cjs):occupied 族起局从固定点 (195,422) 改中线纵向扫点,robust 命中各游戏「开始」按钮 (原固定点落空→死菜单假阴;实测 wanglanmei「开门营业」在 y≈502)。 资产通道(boot-game-host.loadHostAssets + gen.mjs --assets-dir):M3 只决定怎么用资产、不造。 入库工具 publish-amgen.mjs:staged bundle → game_runtime_package(package_json.engineBundle,status=1)→ studio /play。 5 个 M3 footgun 蒸馏进 prompt.mjs + skill(均 wanglanmei-v2 实测翻车): ① scene update 回调签名 (dt) 非 (running,dt) ② ctx.random.next() 非 ctx.random() ③ ctx.time.nowMs() 非 ctx.nowMs() ④ _forensicsView 的 phase/targets 必须在 state() 内实时算(否则定格 boot 快照) ⑤ update(dt) 的 dt 是秒,毫秒计时要 ×1000。 验证:M3 生成 wanglanmei-tycoon-v2(+上述 5 footgun 修复)→ 九门 PASS(score 0→364,distinct 43/43) → 入库 → studio /play/80002/80002 加载+boot+渲染菜单真玩(art 走程序化回退,水彩 art 需引擎资产线 E2/E3)。 (root-fix A 门可信化先于 cbe42933 提交;SAA Java 驱动绑定另track 暂不入此 commit) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.6 KiB
3.6 KiB
_template — A-model 游戏克隆起点(3 层架构)
一个可玩的最小游戏「点圆得分」(menu→play→over)。克隆它、只改 src/game-logic.js(按需拆 core.js/render.js),改成 brief 要的游戏。别从零搭骨架,在 game-logic.js 起点上改最省事。
3 层 + 你只碰 L3
| 层 | 文件 | 你 |
|---|---|---|
| L1 固定 plumbing(write 会被拒) | index.html · entry-bundle.js · src/host-config.js(已注入全 11 插件 + viewport 390×844 + wiring) · src/game.js(薄 wrapper:摊平 opts.runtime → 交 game-logic) |
不碰 |
| L3 你写的游戏本体 | src/game-logic.js(必写) · src/core.js(推荐:纯逻辑,可内联) · src/render.js(推荐:画面) · src/balance.js(可选:数值) · src/assets.js(可选:资产) |
只写这些 |
入口契约(钉死)
// src/game-logic.js —— 你写这个
export function createGame({ plugins, bundle, viewport }) {
// 插件已扁平注入(全 11 件,用到才解构):
const { sceneFsm, sessionScore, hudUi, timerScheduler } = plugins;
// 可用键:sceneFsm sessionScore hudUi timerScheduler save gamefeel juice palettePost audioMusic collision physics
return {
async init(boot) { const ctx = boot.ctx; /* 装资产→定义场景→start;输入不订阅(L1 派发到 handleTap) */ },
update(dt) { bundle.tick(dt); /* ★必调;再推进玩法 */ },
render(g) { /* 画在 g;读 viewport.w/h */ },
handleTap(x, y) { /* ★输入入口:各 phase 点击逻辑;L1 在 pointerdown 时直达调用(别自己订阅输入) */ },
destroy() {},
// 可选·建议:_forensicsView(){ return { state(){…}, measures(){…} }; }
};
}
铁律(check 会拦)
- 入参扁平
{plugins, bundle, viewport}—— 没有 opts、没有 opts.runtime、没有 ctx(L1 已摊平)。受控面 ctx 只在init(boot).boot.ctx(时间/随机/输入/引擎)。 - 插件直接
const {…} = plugins—— 别写opts.runtime.plugins(L1 内部的,你见不到)。游戏只用、不 new、不 register。 update(dt)必调bundle.tick(dt)—— 否则 timer-scheduler/gamefeel 等 onFrame 不推进、timer 永不到期、一局不结束。- 零引擎 import(引擎经
boot.ctx.getEngine());游戏源零裸 Math.random/Date.now/setTimeout/new AudioContext/addEventListener —— 时间随机经 boot.ctx,定时经 timer-scheduler,音效经 plugins.audioMusic;输入不调 ctx.getInput——写实例方法handleTap(x,y)(点击)/handleKey(key)(键盘),L1 的 game.js wrapper 在 pointerdown/keydown 时直达调用(根治"点击消费时机错→启动不了")。 - 插件方法名一律以
src/plugins/<name>/api.d.ts为准,别臆测(scene-fsm 是define不是 defineScenes;particles-juice 没有burst)。 - 画面读
viewport.w/h,别硬编码 390×844。
你写什么 vs 调什么
- 你写(玩法身份):规则/胜负/计分逻辑(core)、画面与命中区(render)、数值(balance)、资产声明(assets)、把它们接到五法(game-logic)。
- 你调(通用机制):计分
sessionScore· 场景机sceneFsm· UI 原语hudUi· 受控定时timerScheduler· 持久save· 手感gamefeel· 粒子juice· 后处理palettePost· 音频audioMusic· 碰撞/物理collision/physics(全已注入,用到才取)。 - 手感:全局氛围(palettePost/gamefeel)自动生效;事件特效(命中/死亡/过关)在事件点调 juice/gamefeel 事件 API(方法名以 api.d.ts 为准)。
详见 .agents/skills/littlejs-game-dev.md(12 插件 API 速查 + ❌→✅ 易犯错)。