guide层v1: 全局向导交互层落地(状态签名earcon/turn cue/静默看门狗/纠错) + 6游戏接线 + 8句向导语音 + 测试全绿

- engine/guide.js 运行时合成8个签名earcon(方法论L2硬参数),off档零定时器,插播queue不顶对白
- settings: guide三档(full/standard/off默认full) + 语速2.2视障档;audio: isVoicePlaying()/setRateBoostEnabled() 清两个已知问题
- 6游戏按schemes§2接线: storyline看门狗+无效手势纠错、simon免扣重听看门狗、escape turn cue+提前卡关提示、rhythm gd_answer/gd_miss+结算看门狗+快进豁免、space首撞指令提示+结算看门狗、rpg格挡纠错+60s催促+状态签名
- 大厅设置面板+向导文档(14-guide-layer.md)+00-global-ux §6;R5台词表补4条一致性
- 试玩转写含向导事件;6 playtest(guide=off断言零改动)+guide.test(34断言)+资产终检全绿
This commit is contained in:
SoundGame Dev
2026-08-30 22:46:05 +08:00
parent 7eb9e7f025
commit 79dffac01e
69 changed files with 2466 additions and 144 deletions
+43 -4
View File
@@ -14,8 +14,9 @@ web/
├── engine/ # 共享引擎 —— 所有游戏复用,勿修改
│ ├── gesture.js # 6 手势识别(触摸+鼠标,内置基础震动反馈)
│ ├── audio.js # Web Audio 三层音频引擎 + 启动遮罩(自动应用用户设置)
│ ├── settings.js # 全局用户设置(voice/speed/bgm,localStorage 持久化)
│ ├── settings.js # 全局用户设置(voice/speed/bgm/guide,localStorage 持久化)
│ ├── haptics.js # navigator.vibrate 震动封装(预定义震型 + iOS 视觉降级)
│ ├── guide.js # 全局向导层(状态签名 earcon / turn cue / 看门狗 / 纠错)
│ ├── game.js # Game / StateMachineGame 基类
│ ├── style.css # 全屏黑底 + 封面铺满 + 屏蔽系统手势 + 闪光降级样式
│ └── vendor/soundtouch.js # SoundTouch 0.3.0(voice 变速不变调,LGPL v2.1)
@@ -137,6 +138,11 @@ new WebAudioEngine(basePath = 'audio/', opts?)
与 stopVoice 的差异:stopVoice 作废整条链,skipVoice 只跳当前句。
游戏层自行绑定手势(如长按/特定滑动的"跳过这句")。
- `stopAll()` — stopBgm(200) + stopVoice()(sfx 很短,不主动停)。
- `isVoicePlaying(): boolean` — 当前是否有对白在播(公开只读;游戏判定
"单击是否跳句"等语境用它,不要读私有字段 `_voice`)。
- `setRateBoostEnabled(on: boolean)` — 长按快进(旁白临时 1.7 倍速)的
per-game 开关,默认开启。rhythm/space 的对局/演奏期传 `false` 豁免
(长按是潜在演奏输入),回菜单/结算/退出时传 `true` 恢复。
三层行为(对齐 Android DefaultAudioEngine):
@@ -175,13 +181,46 @@ BGM。游戏层无需为此写任何判断。
`play('voice/intro')` 依次尝试 `audio/voice/<gender>/intro.{ogg,m4a,mp3,wav}`,
全部找不到再回退平铺的 `audio/voice/intro.*`(过渡期资产未分性别也能播)。
缓存键含性别,游戏中途切换音色下一句即用新性别资产。
- `settings.speed`(`1.0|1.35|1.7`,默认 `1.0`):voice 层 SoundTouch
- `settings.speed`(`1.0|1.35|1.7|2.2`,默认 `1.0`):voice 层 SoundTouch
tempo(**不变调**),在创建播放实例时读取,**从下一句生效**
(进行中的句子不变速——除非长按快进,见上)。
(进行中的句子不变速——除非长按快进,见上)。2.2 为视障档。
- `settings.bgm`(`1.0|0.3|0`,默认 `1.0`):BGM 层独立增益总线。
改动对**进行中的 BGM 立即生效**(150ms 渐变);调到 `0` 立即淡出停止
且此后 `play('bgm/...')` 只记意图不加载不播放;从 `0` 调回非零时,
若此前有想播的 BGM 会自动补播。
- `settings.guide`(`'full'|'standard'|'off'`,默认 `'full'`):向导帮助等级,
`engine/guide.js` 每次动作实时读取(改动即刻生效,无需重启游戏)。
## engine/guide.js — `Guide`(全局向导层)
语音承载语义、earcon 只做粗标记的全局向导:状态签名 earcon(L3)+ 回合标记
(A2)+ 静默看门狗(L4)+ 纠错语音(A4)。设计裁定与 8 句新增语音见
`docs/design/14-guide-layer.md`;逐游戏接线表见 `research/03-schemes/schemes.md` §2。
```js
import { Guide } from '../engine/guide.js';
this.guide = new Guide(audio); // 游戏构造时创建一次
guide.level(); // 'full' | 'standard' | 'off'(实时读 settings.guide)
guide.state('battle'); // 进入宏观状态播签名 earcon(menu/explore/battle/
// choice/settle/danger,全项目统一 8 音词表,
// 运行时 WebAudio 合成,0.25~0.4s,增益 ≈0.12)
guide.turn({ voice, haptic }); // 轮到你:turn earcon + 可选 confirm 轻震 +
// full 档可选语音(queue:true,绝不顶掉在播语音)
guide.watchdog({ arm: true, at: [8000, 20000], say: ['gd_x', null],
onEscalate: (n) => ..., cond: () => ... });
// 静默看门狗:每级到点播 nudge 轻 earcon(+ say[n-1]
// 语音,full 档)再调 onEscalate(n);级数用尽自动停;
// cond()=false 静默取消;任何手势 disarm() 复位
guide.error({ voice, onceKey, skipHaptic }); // 纠错:error 震(游戏层已震则 skipHaptic)
// + full 档语音(onceKey 同局只播一次)
guide.disarm(); // 复位看门狗(手势入口调用)
guide.setEventHook(fn); // 转写 harness 用:fn({type,name,voiceId})
```
- 等级语义:`full` = earcon+语音+震动;`standard` = earcon+震动(无语音);
`off` = 全部 API 静默 no-op,**不挂任何定时器**(回归测试/老手零干扰)。
- 向导语音一律 `queue:true` 插播,绝不打断游戏正在播的内容。
## engine/settings.js — `settings`
@@ -191,7 +230,7 @@ BGM。游戏层无需为此写任何判断。
```js
import { settings } from '../engine/settings.js';
settings.get(); // -> { voice: 'male'|'female', speed: 1|1.35|1.7, bgm: 1|0.3|0 }
settings.get(); // -> { voice: 'male'|'female', speed: 1|1.35|1.7|2.2, bgm: 1|0.3|0, guide: 'full'|'standard'|'off' }
settings.set({ speed: 1.35 }); // 部分更新;非法键/值被忽略(console.warn)
const off = settings.subscribe((snapshot, changed) => { ... }); // off() 退订
```