games-development-ai/tier2/engine/engine-caps-phaser.js
zizi 856a583325 feat(tier2): Phaser 自治生成引擎全栈首落(workflow 9 agent 建·6c6g 静态全过)
override spike-first(创始人 06-23 裁),全栈铺 Phaser tier2 引擎。recon→契约→6模块并行→集成 workflow 产出 ~60 文件,全 6c6g 静态校验过、红线零碰 Tier0/1 产线:
- 契约(6):tier2-source-project.schema / tier2-verdict.schema(fork·round放开·三层校验+富游戏门)/ toolkit 签名 / 探针钩子 / boot-phaser-host.d.ts / mini-肥鹅 fixture 规格
- M1 Phaser scaffold+装载 host+引擎能力面(19;logic-smoke 13/13:五耦合点真接线+赢输双路径+latch不回弹)
- M2 CDP 探针 Phaser 重写+business-sim driver+九门+富游戏三门(5;verdict 过 schema)
- M3 Python 单写 ReAct agent loop+9 工具 toolkit+M3 Anthropic接法+四熔断(9;mini-desktop 真 2.0.2 venv 验:import/9工具/中间件注册 OK)
- M4 datatable schema+金标+资产占位(4)/ M5 prompt+Phaser skill+rag(13)/ M6 成本 RecordingChatModel+trace adapter(3)
- 集成:run_engine.py 入口 + 接线断点已修 + RUN-ON-MINI-DESKTOP.md

待 mini-desktop 真跑(esbuild build + CDP 九门 + M3 生成 = 本质即 0号 spike)。AgentScope 2.0.2 API 逐条对 /root/oss/agentscope 源码核验。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 20:13:44 +00:00

169 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* tier2/engine/engine-caps-phaser.js —— tier2 富游戏引擎受控能力面Phaser 版 · 薄包装 Phaser 内建)
* ownertier2 引擎线M1-Phaser-scaffold+host 消费方①boot-phaser-host注入受控面给游戏 boot②生成 agent 写的 Phaser 工程(经 ctx.getEngine() 拿能力)
*
* ════════════════════════════════════════════════════════════════════════════
* 【这份是什么 · 引擎能力面 Phaser 化】
* 左支Tier0/1·LittleJS的受控能力面在 game-runtime/src/core/api.d.tsEngineCapabilitiesparticles/audio.synth/math
* 由 host-dev/engine-caps.js 背书 LittleJS 内建ParticleEmitter / zzfx / lerp
* 本文件是 tier2 右支Phaser的等价能力面——同一组受控能力粒子 / 音频 / easing / math
* 但底座换成 Phaser 内建薄包装。接口口径对齐 api.d.ts 的 EngineCapabilities但形状 Phaser 化。
*
* 【为什么是受控面而非裸 Phaser】边界模型「引擎可换」铁律对齐 api.d.ts §受控面铁律)
* 生成 agent 写的游戏不直接调 Phaser 裸 API 做粒子 / 发声 / 缓动,而是经本受控面调。
* 理由:① 引擎可换Phaser→Pixi 只换本文件,不改游戏);② 粒子 / 发声是「引擎覆盖的有状态能力」,
* 走受控面句柄统一管理;③ 几何归一化 {x,y},禁裸 Phaser.Math.Vector2 泄漏进游戏。
*
* 【一职一路 · 降级语义】(对齐 api.d.ts §62-68 创始人 2026-06-13 裁)
* - 引擎覆盖的有状态能力particles / audio无 Phaser scene 时headless / node import-safe整面优雅 no-op
* 不留 sim / vendored 降级有意替代会掩盖真接线缺陷F 门要查真调引擎)。
* - 纯数学族math.lerp / smoothStep / easing保内置纯函数 fallback公式恒等非有意替代
* 便于 node 静态校验与 headless 逻辑跑通real 通道下与 Phaser.Math 口径一致。
*
* 【接法】createPhaserEngineCaps({ scene, recHook }) → EngineCapabilities 形状对象。
* scenePhaser.Scene 实例host 在 scene create 后注入;粒子 / 音频 / 缓动经它的 add / sound / tweens 调)。
* recHook引擎能力调用 call-ID 取证钩子F 门真接线证据;缺省 no-op。每次真调 Phaser 内建打一个 call-ID。
* ════════════════════════════════════════════════════════════════════════════
*/
'use strict';
/**
* 创建 Phaser 受控能力面。host 在 scene create 完成后调本工厂建一份,经 boot.ctx.getEngine() 透传给游戏。
* @param {Object} deps 依赖注入
* @param {any} [deps.scene] Phaser.Scene 实例headless / 静态校验时为 null → 有状态能力 no-op、数学族走纯函数
* @param {(id: string) => void} [deps.recHook] 引擎能力调用 call-ID 取证钩子F 门真接线证据;缺省 no-op
* @returns {Object} EngineCapabilities 形状particles / audio / math口径对齐 api.d.ts
*/
export function createPhaserEngineCaps(deps) {
// scene 缺省 nullheadless / node import-safe有状态能力优雅 no-op数学族走内置纯函数 fallback。
const scene = (deps && deps.scene) || null;
// call-ID 取证钩子(缺省 no-op每次真调 Phaser 内建打一个 IDF 门据 __engineCalls 证「真经受控面调引擎、非自绘伪装」。
const recHook = (deps && deps.recHook) || (() => {});
/* ──────────────────────────────────────────────────────────────────────
* ① 粒子受控面(薄包装 Phaser GameObjects.Particles.ParticleEmitter
* 归一化发射器参数(受控面像素 + 中性形态host 映射到 Phaser 发射器返回受控句柄isActive/stop
* 无 scene → 优雅 no-op 句柄(不留 sim 降级对齐左支「null-engine→no-op」
* ────────────────────────────────────────────────────────────────────── */
const particles = {
/**
* 喷一束粒子。
* @param {Object} spec 归一化发射参数({x,y}=受控面像素坐标count=一次性颗数lifeMs/speed/colorStart...
* @returns {{ isActive: () => boolean, stop: () => void }} 受控句柄(封装 Phaser 发射器;游戏只见本面)
*/
emit(spec) {
// 无 sceneheadless / 静态校验):返回 no-op 句柄,不抛错(保 node import-safe不留 sim 降级。
if (!scene || !scene.add || typeof scene.add.particles !== 'function') {
return { isActive: () => false, stop: () => {} };
}
// F 门取证:真调引擎前打 call-ID证真经受控面调 Phaser非游戏自绘伪装
recHook('engine.particles.emit');
const s = spec || {};
// 受控面像素坐标 {x,y} → Phaser 世界坐标Phaser 画布像素坐标系host 不再二次换算,视口已逻辑像素对齐)。
const x = typeof s.x === 'number' ? s.x : 0;
const y = typeof s.y === 'number' ? s.y : 0;
const count = typeof s.count === 'number' ? s.count : 12;
const lifeMs = typeof s.lifeMs === 'number' ? s.lifeMs : 600;
const speed = typeof s.speed === 'number' ? s.speed : 120;
// Phaser 3.60+ 粒子 APIscene.add.particles(x, y, textureKey, config)。
// 纹理键缺省用宿主预置的 1px 白点 '__tier2_particle'host 在 boot 时确保已 generateTexture 注册)。
const emitter = scene.add.particles(x, y, (s.texture || '__tier2_particle'), {
lifespan: lifeMs,
speed: { min: speed * 0.5, max: speed },
scale: { start: typeof s.sizeStart === 'number' ? s.sizeStart : 0.6, end: typeof s.sizeEnd === 'number' ? s.sizeEnd : 0 },
// 一次性 burst发 count 颗后停emitting:false + explode
emitting: false,
});
// burst一次性喷 count 颗Phaser explode
try { emitter.explode(count, x, y); } catch (e) { /* 部分 Phaser 版本签名差异,吞错保不中断 */ }
// 受控句柄:封装 Phaser 发射器,游戏只见 isActive/stop不见 Phaser 对象)。
return {
isActive() {
// Phaser 发射器活跃判定(无 alive 粒子且不发射即非活跃)。
try { return !!emitter && typeof emitter.getAliveParticleCount === 'function' && emitter.getAliveParticleCount() > 0; }
catch (e) { return false; }
},
stop() {
// 停止并销毁(幂等)。
recHook('engine.particles.stop');
try { if (emitter && !emitter.destroyed) emitter.destroy(); } catch (e) { /* 幂等吞错 */ }
},
};
},
};
/* ──────────────────────────────────────────────────────────────────────
* ② 音频受控面(薄包装 Phaser Sound
* 左支是 zzfx 程序化合成EngineAudioSynth.synthSfx/synthSongPhaser 走 sound manager 播放预载音频键。
* 富游戏 spike 期音频是次要先给最小受控播放面playSfx/playBgm无 scene → no-op。
* ────────────────────────────────────────────────────────────────────── */
const audio = {
/**
* 播一个短音效(按预载音频键)。
* @param {string} key 音频键(宿主预载;缺省 / 未载 → 静默 no-op
* @param {Object} [opts] { volume?: number }
*/
playSfx(key, opts) {
if (!scene || !scene.sound || typeof scene.sound.play !== 'function') return;
recHook('engine.audio.playSfx');
try { scene.sound.play(key, { volume: (opts && opts.volume) != null ? opts.volume : 0.8 }); }
catch (e) { /* 未载音频键 / 解码失败:静默 no-op不中断游戏 */ }
},
/**
* 播 / 切背景音乐(循环)。
* @param {string} key 音频键
* @param {Object} [opts] { volume?: number }
*/
playBgm(key, opts) {
if (!scene || !scene.sound || typeof scene.sound.add !== 'function') return;
recHook('engine.audio.playBgm');
try {
const m = scene.sound.add(key, { loop: true, volume: (opts && opts.volume) != null ? opts.volume : 0.5 });
m.play();
} catch (e) { /* 未载 / 解码失败:静默 no-op */ }
},
};
/* ──────────────────────────────────────────────────────────────────────
* ③ 数学受控面(纯函数族 · 口径对齐 Phaser.Math恒可用
* lerp / smoothStep / easing这是「纯数学族」无 scene 也保内置纯函数 fallback公式恒等非有意替代
* real 通道下与 Phaser.Math 口径一致lerp/SmoothStep/Easing便于游戏直接复用引擎曲线又可 headless 跑通。
* ────────────────────────────────────────────────────────────────────── */
const math = {
/**
* 线性插值(口径对齐 Phaser.Math.Linear / LittleJS lerp
* @param {number} a 起点
* @param {number} b 终点
* @param {number} t 比例 [0,1]
* @returns {number}
*/
lerp(a, b, t) {
// 纯函数恒等a + (b-a)*tPhaser.Math.Linear 同口径)。
return a + (b - a) * t;
},
/**
* smoothstep 缓动(口径对齐 Phaser.Math.SmoothStep / LittleJS smoothStep
* @param {number} t 比例 [0,1]
* @returns {number}
*/
smoothStep(t) {
// 钳 [0,1] 后经典 3t²-2t³与 Phaser.Math.SmoothStep(t,0,1) 恒等)。
const x = t < 0 ? 0 : t > 1 ? 1 : t;
return x * x * (3 - 2 * x);
},
/**
* 缓动曲线族(可选 · 对齐 api.d.ts EngineEasing 11 条曲线的子集;纯函数恒可用)。
* 富游戏 UI 动效常用 quadIn/quadOut/cubicOut与 Phaser.Math.Easing 同口径,便于游戏切引擎曲线时无缝。
*/
easing: {
quadIn(t) { return t * t; },
quadOut(t) { return t * (2 - t); },
cubicOut(t) { const f = t - 1; return f * f * f + 1; },
},
};
// 返回 EngineCapabilities 形状particles / audio / math口径对齐 api.d.ts但 Phaser 化。
return { particles, audio, math };
}