feat(cheap-gen): 阶段一B #8+#9 audioMusic 音效放开语义名 + play 强度容错
附A #8:playSfx 原限 blip/thud/chime 三预设,AI 直觉用 'click'/'hit'/'win'/'coin' 等语义名会静默失败。 加 SFX_ALIASES 语义名→预设映射(click/jump/coin→blip、hit/fail/land→thud、win/success/levelup→chime), 未识别名兜底 blip(不静默)——AI 用任意音名都出声。 附A #9:play(intensity) 入参是情绪强度 0..1、非曲目名,AI 误传字符串 → NaN 强度。play/loop 对非数容错 为默认强度;api.d.ts/skill 文档钉死"入参是强度非曲目名,曲子先 loadSong"。 全链路同改:impl(SFX_ALIASES + playSfx 兜底 + play/loop 容错)+ api.d.ts(playSfx 放宽 string + 文档) + skill line118。验证:audio-music 16/16 + 确定性(playSfx 任意名不抛、play 非数不抛)。prompt 不提 audioMusic 故仅 skill。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5cb73a6d82
commit
8196e07c2c
@ -115,7 +115,7 @@ export function createGame({ plugins, bundle, viewport }) {
|
||||
| **gamefeel** | 手感 | `easing`(13 曲线 in/out/inOut);内置 `gamefeel.inputBuffer{consume,peek}`;**工厂(免 new + 自动注入受控 time,#4)**:`gamefeel.createCoyoteTimer(opts)→{setGrounded,isAvailable,consume}`、`gamefeel.createComboWindow(opts)→{hit,getCount}` |
|
||||
| **particles-juice** | 粒子 + 打击感 | `spawnEmitter(preset\|cfg,x,y)`(或 `burst(x,y)` 直发 burst 预设,#6/#10 母语别名);`hitStop(dur)`/`getTimeScale`;`shakeScreen`/`getShakeOffset`;`flashScreen`/`pulseScale`;`PRESETS`(burst/trail/drift)。**⚠️ `render(g)` 必须每帧手调(非自动);`step(dt)` 默认 autoStep 自驱(经 onFrame),要手调 step 须工厂传 `autoStep:false` 防双步进** |
|
||||
| **palette-post** | 调色 + 后处理 | `rgbToHsl/hslToRgb/applyPaletteMap/shiftHsl`;`setPost/renderPost`(vignette/dither/scanline)。**色板由你注入,插件不内置成品色** |
|
||||
| **audio-music** | 程序化音频 | `loadSong/play/stop/loop`;`setIntensity`/`setIntensityLayering`;`playSfx('blip'\|'thud'\|'chime')` |
|
||||
| **audio-music** | 程序化音频 | `loadSong`(载曲)`/play(intensity?)`(播已载曲;**入参是情绪强度 0..1、不是曲目名**,#9)`/stop/loop`;`setIntensity`;**`playSfx(name)`(#8 放开)**:预设 `blip/thud/chime` 或语义名 `click/hit/win/coin/jump/fail` 等(自动映射、未知兜底 blip) |
|
||||
| **save-progress** | KV 持久化 | `get(key,default)/set/remove/clear`;`createMemoryAdapter`/`createLocalStorageAdapter` |
|
||||
| **runtime-probe** | 启动取证(六锚) | `mark(anchor)/getRecords/toJSONL/verify/verifyChain` |
|
||||
|
||||
|
||||
@ -158,7 +158,7 @@ export interface AudioMusicPlugin extends Plugin {
|
||||
* - 无已载入曲谱 → no-op;
|
||||
* - `getAudioContext()===null` → 静默 no-op(告警一次);
|
||||
* - 否则经受控音频上下文渲染并播放。
|
||||
* @param intensity 可选初始情绪强度(0..1),用于按情绪分层决定起始声部增益(缺省 1=全声部)。
|
||||
* @param intensity 可选初始情绪强度(0..1)——**#9:这是强度数值、不是曲目名**(曲子先 loadSong 载入,play 只负责播);非数容错为默认强度。缺省 1=全声部。
|
||||
*/
|
||||
play(intensity?: number): void;
|
||||
/** 停止当前播放(停 song 与所有 loop)。无上下文/未播放时安全 no-op。 */
|
||||
@ -188,10 +188,11 @@ export interface AudioMusicPlugin extends Plugin {
|
||||
*/
|
||||
resolveVoices(intensity: number): VoiceState[];
|
||||
/**
|
||||
* 播放一个内置 ZzFX 音效预设(blip/thud/chime)。无上下文时静默 no-op(告警一次)。
|
||||
* @param name 预设名。
|
||||
* 播放一个音效(#8)。name 可是预设名 blip/thud/chime,也可是常用语义名(click/hit/win/coin/jump/fail 等,自动映射到预设);
|
||||
* 未识别的名兜底播 blip(不静默)。无音频上下文时 no-op。
|
||||
* @param name 预设名或语义名。
|
||||
*/
|
||||
playSfx(name: SfxPresetName): void;
|
||||
playSfx(name: SfxPresetName | string): void;
|
||||
/** 取内置预设的参数包(纯数据,恒可用;便于 node 层 schema 校验与诊断)。 */
|
||||
getPreset(name: SfxPresetName): SfxPreset | undefined;
|
||||
/** 取全部内置预设名(≥3)。 */
|
||||
|
||||
@ -78,6 +78,19 @@ const SFX_PRESETS = Object.freeze({
|
||||
/** 预设名列表(≥3)。 */
|
||||
const SFX_PRESET_NAMES = /** @type {SfxPresetName[]} */ (Object.keys(SFX_PRESETS));
|
||||
|
||||
/**
|
||||
* #8 阶段一B:常用语义音效名 → 三个中性预设的映射(放开 playSfx,让 AI 直觉名 'click'/'hit'/'win' 等可用)。
|
||||
* 三类音色:blip=短促清脆点击 / thud=低频闷响 / chime=明亮带余韵。未列入的名兜底 blip(不静默)。
|
||||
*/
|
||||
const SFX_ALIASES = Object.freeze({
|
||||
// → blip(清脆点击/取得)
|
||||
click: 'blip', tap: 'blip', select: 'blip', beep: 'blip', pop: 'blip', coin: 'blip', point: 'blip', score: 'blip', shoot: 'blip', jump: 'blip',
|
||||
// → thud(撞击/失败/受伤)
|
||||
hit: 'thud', land: 'thud', bump: 'thud', hurt: 'thud', damage: 'thud', crash: 'thud', explosion: 'thud', fail: 'thud', lose: 'thud', gameover: 'thud',
|
||||
// → chime(成功/奖励/升级)
|
||||
win: 'chime', success: 'chime', ding: 'chime', levelup: 'chime', star: 'chime', powerup: 'chime', bonus: 'chime', unlock: 'chime', pickup: 'chime', win1: 'chime',
|
||||
});
|
||||
|
||||
/* ============================================================================
|
||||
* 二、曲谱(Song)数据结构校验 —— 纯逻辑,恒可用
|
||||
* ========================================================================== */
|
||||
@ -453,7 +466,8 @@ export function createAudioMusicPlugin(opts) {
|
||||
},
|
||||
|
||||
play(startIntensity) {
|
||||
startPlayback(false, startIntensity);
|
||||
// #9:入参是「情绪强度 0..1」、不是曲目名(曲子先 loadSong 载入,play 只播);非数容错为 undefined(用默认强度),防误传字符串致 NaN 强度。
|
||||
startPlayback(false, (typeof startIntensity === 'number' && Number.isFinite(startIntensity)) ? startIntensity : undefined);
|
||||
},
|
||||
|
||||
stop() {
|
||||
@ -461,7 +475,8 @@ export function createAudioMusicPlugin(opts) {
|
||||
},
|
||||
|
||||
loop(startIntensity) {
|
||||
startPlayback(true, startIntensity);
|
||||
// #9:同 play——入参是情绪强度 0..1、非曲目名;非数容错为 undefined。
|
||||
startPlayback(true, (typeof startIntensity === 'number' && Number.isFinite(startIntensity)) ? startIntensity : undefined);
|
||||
},
|
||||
|
||||
setIntensityLayering(l) {
|
||||
@ -479,11 +494,9 @@ export function createAudioMusicPlugin(opts) {
|
||||
},
|
||||
|
||||
playSfx(name) {
|
||||
const preset = SFX_PRESETS[name];
|
||||
if (!preset) {
|
||||
logWarn('[audio-music] 未知音效预设:' + String(name));
|
||||
return;
|
||||
}
|
||||
// #8:语义名映射 + 兜底——直接是预设名就用;命中语义别名映射到预设;否则兜底 blip(不静默,AI 用任意名都出声)。
|
||||
const resolved = SFX_PRESETS[name] ? name : (SFX_ALIASES[name] || 'blip');
|
||||
const preset = SFX_PRESETS[resolved];
|
||||
const ac = audioOrSilent();
|
||||
if (ac == null) return; // 静默降级(无音频上下文)。
|
||||
try {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user