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>
169 lines
11 KiB
JavaScript
169 lines
11 KiB
JavaScript
/**
|
||
* tier2/engine/engine-caps-phaser.js —— tier2 富游戏引擎受控能力面(Phaser 版 · 薄包装 Phaser 内建)
|
||
* owner:tier2 引擎线(M1-Phaser-scaffold+host)| 消费方:①boot-phaser-host(注入受控面给游戏 boot);②生成 agent 写的 Phaser 工程(经 ctx.getEngine() 拿能力)
|
||
*
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
* 【这份是什么 · 引擎能力面 Phaser 化】
|
||
* 左支(Tier0/1·LittleJS)的受控能力面在 game-runtime/src/core/api.d.ts(EngineCapabilities:particles/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 形状对象。
|
||
* scene:Phaser.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 缺省 null(headless / node import-safe):有状态能力优雅 no-op;数学族走内置纯函数 fallback。
|
||
const scene = (deps && deps.scene) || null;
|
||
// call-ID 取证钩子(缺省 no-op):每次真调 Phaser 内建打一个 ID,F 门据 __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) {
|
||
// 无 scene(headless / 静态校验):返回 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+ 粒子 API:scene.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/synthSong);Phaser 走 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)*t(Phaser.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 };
|
||
}
|