- research/00-charter.md 研究章程:RQ1-8、多人设×场景矩阵、成功标准 - research/01-literature/ 01/03/04/05 四份调研报告(02 sonification 进行中) - research/03-schemes/game-inventory.md 6游戏状态机与交互接线清单(带行号) - web/harness/ 玩家感知转写器:虚拟时钟mock+台词全文转写+静默/缺资产如实标注 - tools/extract_voice_text.py 台词表→JSON 提取(主表+旧flavor AST解析)
384 lines
41 KiB
Markdown
384 lines
41 KiB
Markdown
# 六款游戏交互接线清单(「向导交互层」接入前置调研)
|
||
|
||
> 调研方法:逐行阅读 `web/engine/{game,gesture,audio,haptics}.js` 与 6 款 `web/<game>/game.js` 全文。
|
||
> 所有行号均指 `web/<game>/game.js`(engine 文件显式标注 `engine/xxx.js:行号`)。以代码为准;与文件头注释/设计文档不一致处已注明。
|
||
> 调研日期:2026-08-30。工作区版本(未提交改动以实际文件为准)。
|
||
|
||
---
|
||
|
||
## 0. 引擎公共事实(先读,后文引用)
|
||
|
||
- 手势集合:`'up'|'down'|'left'|'right'|'tap'|'doubletap'`,另有 `'longpress'|'longpressend'`(gesture.js:5-7)。**tap 回调有 ~300ms 延迟**(双击窗口,engine/gesture.js:11-13、161-166)——即时反馈必须用滑动/双击,rhythm/space/rpg 均做了补偿。
|
||
- 手势识别震动由 gesture.js 内置:滑动→`swipe`、tap/doubletap→`confirm`(engine/gesture.js:144、155、164);语义震动(error/success/warning/hit/beat 等)由游戏层自行调 `vibrate()`。
|
||
- audio 三层:`bgm/`(循环+交叉淡化)、`voice/`(单通道,播 BGM duck 到 20%;`onComplete` 仅自然播完触发,**被打断不触发**)、`sfx/`(即发即弃可叠加)(engine/audio.js:4-11、184-186)。`skipVoice()` = 截断当前句并**触发其 onComplete**(链继续),`stopVoice()` = 作废整条链(engine/audio.js:216-244)。
|
||
- 长按 = 旁白临时 1.7 倍速,audio.js 全局监听 window 事件,游戏零改动(engine/audio.js:97-101、448-458)。rhythm 文件头注明 main/tut 应豁免但引擎无 per-game 开关,属已知限制(rhythm/game.js:36-38)。
|
||
- **`audio._voice` 是私有字段**,无公开"是否有对白在播"查询 API;simon:131-140、escape:328、rhythm:472-476、space:646-648、rpg:1054 都直接读它做"单击跳句"的语境判定——向导层若改引擎应把它转正。
|
||
- 语音链通行写法:各游戏都有自己的 `_speak(ids, onDone)`(session/runId 令牌守卫)+ `_speakGuarded`(看门狗兜底)变体;storyline 例外,用 `playSeq` + 30s 单句看门狗。
|
||
- `Game` 基类(engine/game.js:18-42):`onGesture/start/stop`。`StateMachineGame`(engine/game.js:66-106)**6 款游戏无一使用**,全部直接继承 `Game`、自管状态字段。
|
||
|
||
---
|
||
|
||
## 1. storyline《声命线》(web/storyline/game.js,340 行)
|
||
|
||
### 1.1 状态模型
|
||
- 类 `StorylineGame extends Game`(:84)。**手动状态字段,非状态机基类**。
|
||
- 字段(:87-101):`mode: 'STORY'|'MENU'`(:76);`nodeId`(剧情节点 id,NODES 表 :31-68 共 32 节点);`voicePlaying`(链播放中);`currentVoiceId`;`promptPlaying`(当前句是抉择提示 `voice/p\d*`,正则 :82);`choiceArmed`(提示音播完、可左右抉择);`pendingChoice`(提示音播放中缓存的抉择);`awaitingRestart`(结局播完);`seqToken`(语音链作废令牌);剧情标记 `companion/knowTrap/betrayed`。
|
||
- 流转驱动:**语音 onComplete 链 + 手势**。`playNode`→`playSeq`(:175-196, 230-261)串行播完→`onNodeSeqDone`(:214-227)置 `choiceArmed/awaitingRestart` 或静默等 tap;`choose`(:263-273)驱动跳节点;无任何游戏逻辑定时器(只有语音看门狗)。
|
||
|
||
### 1.2 等待输入点清单
|
||
| # | 状态/条件 | 玩家该做什么 | 此刻声音 | 位置 |
|
||
|---|---|---|---|---|
|
||
| W1 | 叙事节点链播完(`voicePlaying=false`、`node.next≠null`) | tap=继续下一节点;doubletap=重听本节点;up=帮助;down=菜单。**left/right 静默忽略**(:137 prompt==null return) | 完全静默(仅 `bgm/dark_drone` 循环垫底,:107) | :214-227("否则等待单击继续" :226),tap 分支 :130 |
|
||
| W2 | 选择节点提示音播完(`choiceArmed=true`,p1~p8 共 8 个节点) | left/right=抉择。**tap 无效**(选择节点 `next==null`,:130 判空) | 静默 | :217-222、:137-141 |
|
||
| W3 | 结局播完(`awaitingRestart=true`,e1~e4) | tap=重开 | 静默 | :224、:126 |
|
||
| W4 | 菜单(`mode=MENU`) | left=重开;right/tap=关闭;up=帮助;doubletap=重听菜单语音。**down 无绑定** | `bgm/menu`(:304)+ `voice/menu` 播完即静默 | :151-171 |
|
||
| W5 | (特殊)提示音播放中滑 left/right | 不生效,**缓存进 `pendingChoice`**,播完自动执行(缓冲,非忽略) | voice/p* 播放中 | :139、:218-222 |
|
||
|
||
### 1.3 播放内容但不接受输入的时刻
|
||
- 节点语音链播放中(`playSeq` :230-261):tap=跳过当前句(`skipVoice`,**`promptPlaying` 时不可跳**,盲玩公平,:128-129);doubletap=打断重听(:198-211);up/down 随时可用。
|
||
- 引导句 `voice/skip_hint`(首局 n00 后追加,:188-191)、彩蛋 `e1_child`(:193)随链播放,可跳。
|
||
- 输入行为总述:**从不报错**,不匹配的手势一律静默 return(:130-141 各分支)。
|
||
|
||
### 1.4 错误路径清单
|
||
- **无"错误"概念**:无效手势(非选择节点滑 left/right、选择节点按 tap、菜单按 down)全部静默忽略,无震动、无音效、无语音。代码中无一处 `vibrate('error')`。
|
||
- 唯一"负反馈"是结局本身的剧情音效(e3 `sfx/radio_static` :66、e4 `sfx/struggle` :67)。
|
||
- **无纠错性语音**(也不需要传统意义的纠错,但"无效手势零反馈"对盲玩家等于手势没发出去)。
|
||
|
||
### 1.5 已有帮助/状态播报
|
||
- up = `playHelpAndResume`(:284-297):`seqToken++` 作废当前链 → 播 `voice/help` + `voice/status1..3`(按 `actOf` :331-339 分幕)→ **自动重播当前剧情节点**。这是全游戏唯一的状态播报,内容是"第几幕",不含节点级进度。
|
||
- 菜单内 up 同函数(`backToMenu=true`,:160-162)。
|
||
- 无卡关看门狗:某节点静默等 tap 永远不会超时(除 :245-250 的 30s 单句语音资产缺失看门狗)。
|
||
|
||
### 1.6 超时/定时器
|
||
| 定时器 | 时长 | 后果 | 位置 |
|
||
|---|---|---|---|
|
||
| 单句语音看门狗 `VOICE_WATCHDOG_MS` | 30s | 视为资产缺失,自动续链下一句 | :79、:245-250 |
|
||
| (无其他定时器;无卡关/保护/倒计时) | | | |
|
||
|
||
### 1.7 向导层接入点建议
|
||
1. **状态播报**:向导层劫持 `playSeq`(:230)即可掌握全部旁白时机;"你现在的进度"语音可在 `onNodeSeqDone`(:214)后追加(在 voice 链尾部排队,不与 p* 提示冲突——p* 是链的末句,播完即 W2,向导层应避免在此追加,见下)。
|
||
2. **轮到你提示**:W1(叙事节点播完)在 `onNodeSeqDone` :226 分支插"轻触继续"提示(现状完全静默,是全 6 款中最盲的等待点)。W2 不可插语音(会破坏"提示音播完即可选"的契约),可改用**震动**(`vibrate('confirm')`)标记 `choiceArmed=true`(:217)。
|
||
3. **静默看门狗**:W1/W2/W3 均无超时。建议在 `onNodeSeqDone`(:214)挂 N 秒无手势→重播 `voice/help+status`(复用 `playHelpAndResume` :284)。W3 同理(`awaitingRestart` 置位点 :224)。
|
||
4. **纠错反馈**:`onStoryGesture`(:121)各静默 return 处(:137 等)加轻震动(`swipe` 之外的语义提示)或一句 `voice/` 提示,成本极低。
|
||
|
||
---
|
||
|
||
## 2. simon《音序回响》(web/simon/game.js,547 行)
|
||
|
||
### 2.1 状态模型
|
||
- 类 `SimonGame extends Game`(:66)。
|
||
- `phase` 枚举(:69):`idle|tutorial|announce|playback|input|roundend|gameover|menu`。辅助字段:`round`(序列长 = round+1)、`inputIdx`、`chances`(重听机会,初始 1、每 4 轮 +1、上限 2,:73)、教学字段 `teachIdx/teachAwait/teachErrs`(:76-78)、`_menuIdx`、`_session`(会话令牌)、`_timers`(:80-82)。
|
||
- 流转驱动:**定时器 + onComplete 链 + 手势**。教学:`_teachStep`(:151-175)语音链播完→播音色→900ms→`t_try`→播完或 1.5s 兜底→`teachAwait=true` 等手势;对局:`_nextRound`(:236-259)→announce 语音(2.5s 兜底)→`_playSequence`(:290-319)定时器驱动的序列演示→400ms→`confirm` 震→第 1 轮 `your_turn` 语音→`_toInput`(:321)等输入。
|
||
|
||
### 2.2 等待输入点清单
|
||
| # | 状态/条件 | 玩家该做什么 | 此刻声音 | 位置 |
|
||
|---|---|---|---|---|
|
||
| W1 | tutorial `teachAwait=true` | 做当前教的手势(6 个逐一);down=跳过教学(教 down 时除外,:180 上下文判定) | 静默(t_try 播完后) | :151-175、:177-216 |
|
||
| W2 | `phase='input'` | 按序重复序列手势;doubletap 在"非期望双击"时=主动重听(耗机会,:329-332) | **完全静默**(对局全程无 BGM,文件头 :7-9);进入时仅 `confirm` 震 + 第 1 轮 `voice/your_turn` | :321-324、:302-315 |
|
||
| W3 | `gameover` 播报链播完 | tap=重开;up=规则;down=菜单 | `bgm/menu` vol0.3(起播后 ~2650ms 淡入,:418-422) | :109-113 |
|
||
| W4 | `menu` | tap=循环项;doubletap=确认;up=规则。**down 无绑定** | `bgm/menu` | :449-462 |
|
||
|
||
**W2 无任何超时**:玩家不动则永远静默等下去(无看门狗)——向导层最大价值点之一。
|
||
|
||
### 2.3 播放内容但不接受输入的时刻
|
||
- `announce`(轮次整句 `round_01..round_20`/`round_more`,:245-256):**所有手势静默忽略**(:117-119 default 分支,裁定见 :118 注释"纯聆听阶段手势静默忽略");2.5s 兜底直接进 playback。
|
||
- `playback`(序列演示,每音 `beat` 震,间隔 `max(420, 700-(round-1)*26)`ms,:285-319):同样全部忽略(含第 1 轮 your_turn 期间)。
|
||
- `roundend`(答对/答错后的播报链,`_speakGuarded` 10s):忽略;播完自动 `_nextRound` 或重播序列。
|
||
- 旁白跳过:`_tapSkipsVoice`(:131-140)按 phase 白名单(tutorial 非等待期/announce/roundend/gameover 可跳;**teachAwait 期 tap=答题、playback/input 期不跳、menu 单击是菜单语义**)。
|
||
|
||
### 2.4 错误路径清单
|
||
- 教学做错(:205-215):`vibrate('error')` + `voice/t_wrong_<gesture>`;**同一步错满 3 次**:`vibrate('warning')` + `voice/t_blunt_<gesture>`(更直白描述)并计数清零。→ 已有纠错语音且带升级。
|
||
- input 做错(`_wrongAnswer` :377-391):有机会→`error` 震 + 300ms 后 `warning` 震 + `voice/retry_use`(+归零时 `chances_out`)→ 自动重播同一序列(`phase='roundend'`)。无机会→`_gameOver`(:393-423):`error` 震、300ms 后 `hit` 震、700ms 后 `sfx/gameover`、1400ms 后播 `gameover+score_band+new_record+replay+rules_prompt` 链。
|
||
- 重听无机会(:344-349):`error` 震 + `voice/chances_out`。
|
||
- 对局输入错误无"该做什么"级纠错语音(retry_use 只说"再试一次",不重复期望手势——靠重播序列弥补)。
|
||
|
||
### 2.5 已有帮助/状态播报
|
||
- gameover up / menu up = `_speakRules`(:435-437):`voice/rules + voice/rules_prompt`。**tutorial 与对局中无帮助手势**(up/down 在 input 是序列手势)。
|
||
- 状态播报:每轮 `round_XX` 整句(主动播)、成绩 `score_band1..6`(结算)、机会变动 `chance_plus/chances_out`——播报文化完整。
|
||
- 对局无卡关计时器(等输入无限等,见 2.2)。
|
||
|
||
### 2.6 超时/定时器
|
||
| 定时器 | 时长 | 后果 | 位置 |
|
||
|---|---|---|---|
|
||
| 开场进教学延迟 | 500ms | `_runTutorial` | :92 |
|
||
| `t_try` 缺失兜底 | 1.5s | 直接 `teachAwait=true` | :169-171 |
|
||
| 教学跳过(down)兜底 | 4s | `_beginGame` | :185 |
|
||
| announce 兜底 | 2.5s | 直接 `_playSequence` | :258 |
|
||
| 序列末音→"轮到你" | 400ms | `confirm` 震 + 第 1 轮 your_turn | :315、:304 |
|
||
| your_turn 兜底 | 2.5s | 直接 `_toInput` | :311 |
|
||
| roundend→下一轮 | 300ms | `_nextRound` | :373 |
|
||
| `_wrongAnswer` warning 震 | 300ms 后 | 第二段震动 | :382 |
|
||
| gameover 各段 | 300/700/1400/2650ms | hit 震/gameover 音/语音链/menu pad | :404-422 |
|
||
| `_speakGuarded` 默认 | 10s | 语音缺失也推进链回调 | :518-528 |
|
||
| 教学收尾 t7 链兜底 | 12s | `_beginGame` | :201 |
|
||
|
||
### 2.7 向导层接入点建议
|
||
1. **静默看门狗(最优先)**:`_toInput`(:321)挂计时——N 秒无输入先播提示再自动重听(复用 `_relisten` :344 逻辑但可免机会扣减或改为无限次提示);`gameover` 链播完(:413 `_speak(ids, null)` 的 null 回调处)同理。
|
||
2. **轮到你提示泛化**:`_playSequence` :304 的 `confirm` 震 + :306 `your_turn`(仅第 1 轮)——向导层把"轮到你"语音做成低打扰版(震型升级或每 K 轮播一次)只需改这两行。
|
||
3. **状态播报**:`phase` 字段完备,向导层可直接读;劫持 `_speak`(:502)即可统一插播。
|
||
4. **菜单缺口**:对局中(input/playback)无菜单呼出路径,唯一退出是故意答错到 gameover——向导层或需一个不与 6 手势冲突的呼出方案(长按已全局占用为快进,engine/gesture.js:14-19)。
|
||
5. `audio._voice` 私有读(:131-140)应随引擎转正。
|
||
|
||
---
|
||
|
||
## 3. escape《暗室》(web/escape/game.js,672 行)
|
||
|
||
### 3.1 状态模型
|
||
- 类 `EscapeGame extends Game`(:76)。`Phase` 枚举(:36-42):`REOPEN|GUIDE|PLAY|ENDING|MENU`。
|
||
- 字段(:86-110):坐标 `x,y`(3x3)、谜题旗标 `hasKey/hasFuse/cabinetOpen/powerOn`、`visited`、`guideStep 0..7/guideSwipes/guideErrs/_guideBusy`、卡关 `_lastProgressAt/_stuckShown(L1|L2|L3)/_lastL3At`、`_voiceSkippable`、`_menuReturn/_menuIdx`、`_session`。
|
||
- 流转驱动:**手势为主 + 语音 onComplete 链**。start 按存档进 REOPEN 或 GUIDE(:121-128);GUIDE 由 `_advance`(:176-194)语音播完回调推进 guideStep;PLAY 是自由探索无严格状态;谜题链阶段由派生函数 `_stage()`(:454-459:key→cabinet→box→gate)给出。
|
||
|
||
### 3.2 等待输入点清单
|
||
| # | 状态/条件 | 玩家该做什么 | 此刻声音 | 位置 |
|
||
|---|---|---|---|---|
|
||
| W1 | REOPEN:`reopen_prompt` 播完(**不可跳**) | tap=直接开始;up=重听引导。左右滑/双击静默忽略 | 静默(`bgm/drone` 垫底,:120) | :134-137 |
|
||
| W2 | GUIDE 各步:推进语音播完(`_guideBusy=false`) | 做该步手势(tap/滑动×2/左/下/tap/双击) | 静默 | `_advance` :180-187,`_onGuideGesture` :196-256 |
|
||
| W3 | PLAY 常态 | 四向移动/tap 检查/doubletap 用物 | `amb_*` 环境音(进房自动播,:370-372)+ `bgm/drone` | `_onPlayGesture` :318-336 |
|
||
| W4 | PLAY:voice 播放中 | **滑动移动不受阻**(无全局 gate);仅 tap 分流:可跳句=skip,不可跳句=**忽略**(:328-331 注释解释了为何不触发检查) | voice | :324-332 |
|
||
| W5 | ENDING:结算链播完(或 30s 兜底) | tap=重开;down=菜单 | `bgm/menu` vol0.5(`_startEndingBgm` :577-581) | :144-147 |
|
||
| W6 | MENU | tap=循环;doubletap=确认;up=操作说明;down=关闭 | `bgm/menu` | :524-539 |
|
||
| W7 | GUIDE G7:g7_goal 播放中 | 手势忽略(等 `_advance` onDone 进 PLAY) | voice | :253-254 |
|
||
|
||
### 3.3 播放内容但不接受输入的时刻
|
||
- 引导推进链(`_speak(..., {skippable:false})`,:192):`_guideBusy=true` 期**下滑以外的手势一律忽略**(:199-200);下滑随时可跳过引导(G4 除外——G4 答案就是下滑,:197-199)。
|
||
- 关键获得句不可跳:`found_key`(:390)、`unlock_cabinet`(:422)、`fix_box`(:431)、逃脱结算链(:570-571)、L3 带路链(:489)、`guide_skip`(:268)。
|
||
- 可跳句:`room_*` 首到描述、`start_room`、`empty_*`、检查描述、`wall`、L1/L2 提示。
|
||
- PLAY 期**没有缓冲**:voice 中滑动手势立即生效(移动与语音并行),tap 按可跳性分流。
|
||
|
||
### 3.4 错误路径清单
|
||
- 撞墙(`_move` :341-346):`sfx/wall` + `vibrate('error')` + `voice/wall`(**每次都播**,:344 注释"取消一次性限制")。
|
||
- 引导做错(`_guideWrong` :278-288):`vibrate('error')` + `guide_wrong_<dir>`(方向错)/`guide_wrong_gesture`(手势类型错);**错满 3 次**:`warning` 震 + `guide_blunt_<dir>` 直白重述(仅方向步有 blunt)。→ 完整纠错语音 + 升级。
|
||
- 双击无可用目标(`_useItem` :435-443):`error` 震 + 播 `use_fail`(关键房间)/背包句 `inv_*` + **主动追加当前阶段 L2 明示** `stuck_<stage>_2`。→ 已有"告诉玩家该去哪"级纠错。
|
||
- **纠错性语音:全 6 款中最完备**(wrong/blunt/stuck 三级 + nav 带路)。
|
||
|
||
### 3.5 已有帮助/状态播报
|
||
- **PLAY 中无帮助手势**(四向/tap/doubletap 全被占用,上滑=向北走)。状态查询只能靠:tap 重播环境音+描述(:375-409)、doubletap 兜底播报背包+L2(:435-443)。
|
||
- MENU up = `voice/help_ops`(:529-530)。
|
||
- **三级卡关看门狗**(`_checkStuck` :479-500,每 5s 轮询 :463-466;进展事件 `_progressEvent` :475-477 重置):60s L1 `stuck_ask+stuck_<stage>_1`(暗示);120s L2 `stuck_<stage>_2`(明示);180s L3 `stuck_<stage>_3` + 按坐标实时算路 `nav_*`,此后**每 60s 重复**(:485-490)。全部 `warning` 震;L3 不可跳。
|
||
|
||
### 3.6 超时/定时器
|
||
| 定时器 | 时长 | 后果 | 位置 |
|
||
|---|---|---|---|
|
||
| 卡关轮询 | setInterval 5s | 见 3.5 | :465 |
|
||
| L1/L2/L3 阈值 | 60s/120s/180s,L3 重复 60s | 提示/明示/带路 | :483-499 |
|
||
| 检查描述延迟 | 800ms | 先环境音后描述 | :379-381 |
|
||
| 引导推进看门狗 | 20s | 语音缺失也切步(带 `guideStep===fromStep` 防误触发) | :188-190 |
|
||
| 跳过引导兜底 | 5s | `_enterPlay` | :270 |
|
||
| 逃脱结算 pad 兜底 | 30s | `_startEndingBgm` | :573 |
|
||
|
||
### 3.7 向导层接入点建议
|
||
1. **三级卡关即现成的静默看门狗骨架**:向导层接管 `_checkStuck`(:479)或把阈值/级别改由向导层配置;`_progressEvent`(:475)就是标准的"重置看门狗"钩子。
|
||
2. **轮到你提示**:W1/W2 播完即静默,可在 `_advance` done(:180-187)和 REOPEN(:126)后加轻提示;PLAY 是开放探索不需要。
|
||
3. **帮助缺口**:PLAY 无帮助手势——向导层的"状态播报"可挂到向导专属入口(如连续 N 次撞墙后主动播 `nav`),不必新增手势。
|
||
4. `_voiceSkippable`(:96、:642-645)是全项目最干净的"可跳性"封装,向导层插播语音时应复用该机制决定自己的句子可否被打断。
|
||
|
||
---
|
||
|
||
## 4. rhythm《节拍行者》(web/rhythm/game.js,767 行)
|
||
|
||
### 4.1 状态模型
|
||
- 类 `RhythmGame extends Game`(:133)。`phase` 枚举(:136):`idle|intro|tut|select|main|menu|end`。
|
||
- 字段:`_runId`(作废令牌)、`_timer`(40ms 调度泵句柄)、`_sched`(领奏音符绝对时刻)、`_timed`(mid_song 等定时动作)、`_cues`(应答期望 `{gesture, expected, judged, result, phrase}`)、`_bgmSrc/_bgmT0`(手动 BGM 网格)、`_tutIdx/_tutFails/_tutAttempts`、`_menuIdx/_pauseAt/_bgmOffset`、`_phaseEnd`(实例字段,constructor 未声明,:308/:421 赋值)。
|
||
- 流转驱动:**AudioContext 时钟绝对排程 + 40ms 泵(`_pump` :239-268)+ 手势**。无 setTimeout 链播(语音仍走 onComplete `_speak`)。全部节拍按 `ctx.currentTime` 预排(LOOKAHEAD 0.6s,:46)。
|
||
|
||
### 4.2 等待输入点清单
|
||
| # | 状态/条件 | 玩家该做什么 | 此刻声音 | 位置 |
|
||
|---|---|---|---|---|
|
||
| W1 | tut 应答小节(领奏后 4 拍,`cue.expected`±GOOD) | 按谱跟打(T1 单音→T2 左右→T3 三音) | 领奏音已停,`bgm/track90` 循环 | `_tutLead` :290-310 |
|
||
| W2 | select 常态 | 左右换曲/tap 确认(未解锁播 `song_locked`)/up=规则/down=回大厅 | `bgm/menu` + 选项播报 | :366-384 |
|
||
| W3 | main 每个 cue 应答窗口(expected±0.25s) | 对应乐器手势 | 领奏 1 小节后 4 拍空窗 + 曲目 BGM | `_onMainGesture` :495-527 |
|
||
| W4 | main 长空档(restAfter 休止小节) | 无期望输入(可自由演奏/下滑呼菜单) | BGM | 谱面 :61-116 |
|
||
| W5 | end 结算链播完 | tap=同曲再来;right=下一首/locked/最后一首播 `voice/restart`;down=回大厅 | `bgm/menu`(链播完淡入,:679) | :452-459 |
|
||
| W6 | menu | tap=循环(resume/lobby 两项);doubletap=确认;up=帮助;down=恢复 | **静默**(模态暂停、断点续播不叠 pad) | :569-608 |
|
||
| W7 | intro(welcome 播放中) | **只收 tap 跳旁白**,其余全忽略 | voice + 6s 后起的 track90 | :461-463、:173 |
|
||
|
||
**注意**:main 阶段"轮到你"无任何显式信号——领奏→应答的切换只靠 BGM 网格与 4 拍静默空窗自悟(对比 simon 有 confirm 震+your_turn、rpg 有 turn_player 标点)。向导层第二价值点。
|
||
|
||
### 4.3 播放内容但不接受输入的时刻
|
||
- intro:非 tap 手势全部忽略(:461-463)。
|
||
- tut/select/end:tap=逐句跳过(`_trySkipVoice` :472-476),其余手势按阶段语义处理(tut 中会记尝试 :479-493——**教学领奏期的手势不是忽略而是全量记录进评估**)。
|
||
- main:**永不跳语音**(mid_song 不可跳裁定,:447 注释);所有手势按演奏判定——窗口内错手势=Miss(:518-520),窗口外=自由演奏发声不判定(:514-517)。
|
||
|
||
### 4.4 错误路径清单
|
||
- Miss(窗口内错手势/超时缺失):**不播乐器音**(错过=没出声)+ `vibrate('error')`(:548-556);超时判定在泵里 :258-263(tap 余量 TAP_DELAY+0.08,其余 0.05s)。**无任何语音反馈**——对局期纠错全靠震,方向都不提示。→ 向导层最该补的位置。
|
||
- 教学评估失败(`_tutEvaluate` :313-354):`error` 震 + 分类纠错语音:手势错=`retry_gesture`、过早=`retry_early`、过晚=`retry_late`、没出手=`retry_listen`(:347-353);**同一步失败 2 次 `retry_skip` 自动跳过**(:335-346)。
|
||
- 未解锁曲 tap:`voice/song_locked`(:373-377)。
|
||
- 不掉血不中断(文件头 :15,"歌照唱完")。
|
||
|
||
### 4.5 已有帮助/状态播报
|
||
- select up = `voice/rules`(:381-383);menu up = `rules + settings_hint`(:585-587)。**main 中无帮助**(下滑被菜单折中占用,:498-501:仅当无悬置/临近 down 音符时下滑才呼菜单,`_downNotePending` :530-534)。
|
||
- 状态播报:无"你打到第几乐句"类播报(节奏游戏特性,可理解);结算链完整(`grade_s..c`+`new_best_combo`+unlock+`restart`)。
|
||
|
||
### 4.6 超时/定时器
|
||
| 定时器 | 时长 | 后果 | 位置 |
|
||
|---|---|---|---|
|
||
| 调度泵 | setInterval 40ms | 排程音符/Miss 判定/乐句结束 | :270-273 |
|
||
| `_speakGuarded` | 默认 10s;welcome 8s | 语音缺失兜底推进 | :756-766、:174 |
|
||
| 开场 BGM 延迟 | 6.0s(教学)/1.0s(正式) | 网格起点 | :173、:400 |
|
||
| tap 判定补偿 | -0.30s | 手势时刻修正 | :483、:504 |
|
||
| `_phaseEnd` | 末 cue + 0.25 + 0.3 + 0.4/0.5s | 教学/曲目结束 | :308、:421 |
|
||
| 暂停平移 | resumeAt=now+0.1s | 全部绝对时刻 shift(断点续播) | :595-608 |
|
||
| 结算 BGM 淡出 | 0.6s ramp/700ms stop | 曲目 BGM 收 | :634-643 |
|
||
|
||
**注意**:`_finish` 的结算链用 `_speak`(无看门狗,:679)——`grade_*` 等语音资产若缺失,`bgm/menu` 永远不会淡入(W5 永远纯静默等待)。这是与 space `_gameOver` 同款的代码事实缺口。
|
||
|
||
### 4.7 向导层接入点建议
|
||
1. **对局纠错语音**:`_applyMiss`(:548)加连续 N 次 Miss 触发一次 `voice/` 提示(素材可参考教学 `retry_gesture`);插播通道建议复用 `play('voice/…', {queue:true})`(engine/audio.js:186)避免顶掉 mid_song。
|
||
2. **轮到你提示**:在泵里检测"首个未判定 cue 进入窗口"(`_pump` :244 附近可取 `_cues` 首个 `!judged`),首乐句播一次 `your_turn` 型提示;或谱面排程时向 `_timed` 推入(:414-418 旁已有 mid_song 先例)。
|
||
3. **结算链补看门狗**:`_finish` :679 `_speak`→`_speakGuarded`(守卫里起 `bgm/menu`)。
|
||
4. **状态播报**:select/end 已够;main 中向导层若要插播必须走 `queue:true` 且避开 `_downNotePending` 类判定干扰。
|
||
|
||
---
|
||
|
||
## 5. space《盲航》(web/space/game.js,698 行)
|
||
|
||
### 5.1 状态模型
|
||
- 类 `SpaceGame extends Game`(:88)。`phase` 枚举(:91):`idle|ask|drill|playing|feature|menu|over`。
|
||
- 字段(:97-116):`hits/dodges/runStart/protectionUntil/_protectAnnounced/swipes[]`(滑动手势环形缓冲)、`obstacles`(Set,`{dir, impactAt}`)、`drillOb/drillIdx/drillFails`、`shieldUpUntil/shieldReadyAt/boostUntil/boostReadyAt`、`boostIntroDone/shieldIntroDone/featureKind`、`_menuIdx`、`_session`。
|
||
- 流转驱动:**纯 setTimeout(`_after` :679-687,全部带 session 守卫)+ 手势**。首局 D0-D5 演练(`_startDrill` :178);对局 `_startRun`(:268-281)排三个一次性定时器:20s 单击引入、45s 双击引入、60s 保护期结束宣告;刷怪自递归 `_scheduleSpawn`(:423-431)。
|
||
|
||
### 5.2 等待输入点清单
|
||
| # | 状态/条件 | 玩家该做什么 | 此刻声音 | 位置 |
|
||
|---|---|---|---|---|
|
||
| W1 | ask:`second_run_ask` 播完 | tap=直接出发;up=重听演练。其余静默忽略 | 静默(无 BGM) | :131-134、:150-153 |
|
||
| W2 | drill:每步 `drill_*` 旁白播完→演练陨石逼近 4.5s | 同向滑动拨开(D1 左/D2 右/D3 上/D4 下) | `whoosh_<dir>`(语音先、音效后不叠放,:203 注释) | `_nextDrill` :191-204、`_spawnDrillOb` :206-214 |
|
||
| W3 | drill:无 drillOb(旁白/反馈播放中) | down=跳过演练;tap=跳句;**其余忽略** | voice | :217-223 |
|
||
| W4 | playing 常态 | 持续同向闪避;tap=加速(20s 引入后);doubletap=护盾(45s 引入后);down=无下方陨石时呼菜单(:295-301 折中) | `bgm/engine` + whoosh + 两档 alarm | `_onPlayGesture` :287-324 |
|
||
| W5 | feature:引入句播完后 8s 内 | 做 tap(boost 教学)或 doubletap(shield 教学);其他手势忽略等超时 | 静默(刷怪暂停、陨石清空,:347-349) | `_beginFeature` :339-352、`_onFeatureGesture` :354-374 |
|
||
| W6 | menu | tap=循环(resume/lobby/help 三项);doubletap=确认;up=帮助;down=恢复 | `bgm/menu` 300ms 快切 | :526-540 |
|
||
| W7 | over:结算链播完 | tap=再飞(voice 中 tap=跳句,:167-170);down=回大厅 | `bgm/menu`(链播完淡入,:573-575) | :166-172 |
|
||
|
||
### 5.3 播放内容但不接受输入的时刻
|
||
- 演练旁白(`_speakGuarded` 链)、`feat_boost/feat_shield` 引入句、对局归因句 `hit_*`/`protect_*`、里程碑 `ms_*`、结算链:播放中 tap=跳句(:221、:305、:357、文件头 :23-25),其余手势按上文 W3/W5 分流。
|
||
- playing 中**滑动闪避从不被 voice 阻塞**(:290-293 无 gate)。
|
||
- feature 期其他手势"忽略,等超时自动继续"(:359 注释)。
|
||
|
||
### 5.4 错误路径清单
|
||
- 演练做错/超时(`_drillFail` :243-256):`vibrate('error')` + `voice/drill_retry_<dir>`(当场重教重演);**失败 2 次 `drill_moveon` 自动带过**不卡流程。
|
||
- 保护期被撞(`_resolve` 第 4 级 :485-490):`sfx/shield_block` + `warning` 震 + `voice/protect_<dir>`(归因:哪边来的、你该怎么拨)。
|
||
- 正常被撞(:492-501):`sfx/hit` + `hit` 震 + `voice/hit_<dir>`;第 2 次追加 `hit_last`;第 3 次坠毁 `_gameOver`(:556-577):`error` 震、300ms 后 `hit` 震、`sfx/crash`、1200ms 后结算链 `time_1..4 + new_record + dodge_1..4 + restart_hint`。
|
||
- **纠错性语音:归因式**(告诉方位)而非指令式("向左滑")——演练期有指令式(drill_retry 说反了方向语义吗?见语音资产,代码只按 dir 选 id)。
|
||
- **注意**:`_gameOver` 结算链 `_speak(ids, ...)` 无看门狗(:573-575)——`time_*` 资产缺失则 `bgm/menu` 永不淡入。
|
||
|
||
### 5.5 已有帮助/状态播报
|
||
- menu up / menu 第三项 = `help_recap + settings_hint`(:530-532、:538)。**playing 中无帮助手势**(手势全是闪避)。
|
||
- 状态播报:保护期结束宣告 `protect_end`(:409-415,含 `_rearmRunTimers` :389-405 的暂停补偿补播);护盾充能完毕 `shield_ready`+`pickup` 震(:327-334);里程碑 10/25/50/100(:80、:504-512);受击次数走 `hit_last`。状态可辨识性对局内良好。
|
||
|
||
### 5.6 超时/定时器(全项目最多)
|
||
| 定时器 | 时长 | 后果 | 位置 |
|
||
|---|---|---|---|
|
||
| 保护期 | 60s | 撞击不掉血;结束播 `protect_end`+`warning` 震 | :56、:275-280、:409-415 |
|
||
| 单击/双击引入 | 第 20s/45s | 暂停刷怪进 feature | :60-61、:278-279 |
|
||
| feature 等待上限 | 8s | 不做也继续(`_endFeature`) | :62、:351 |
|
||
| 刷怪间隔 | 保护期 4000ms;之后 3200→1200ms(120s 爬满) | `_scheduleSpawn` 自递归 | :57-58、:67、:423-431 |
|
||
| 逼近时长 | 保护期/演练 4000/4500ms;之后 2800→1800ms | 撞击判定点 | :57-59、:436 |
|
||
| 警报两档 | 撞击前 1000ms/500ms | `alarm` vol0.5→1 + `danger` 震×2 | :50、:442-453 |
|
||
| 闪避窗口/宽限 | 1500ms / 250ms | `_resolve` 匹配 swipes | :48-49、:469-471 |
|
||
| 护盾 | 持续 6s / 充能 30s(完毕播报) | | :51-52、:327-334 |
|
||
| 加速 | 持续 1.8s / 冷却 3s | | :53-54 |
|
||
| 演练陨石 | 4500ms 超时=做错 | `_resolveDrill` | :59、:213、:238-241 |
|
||
| 坠毁结算延迟 | 1200ms | 爆炸声后开口 | :567 |
|
||
| `_speakGuarded` | 默认 10s | 演练/收尾链兜底 | :667-677 |
|
||
|
||
### 5.7 向导层接入点建议
|
||
1. **死亡教学升级**:`_resolve` :492-501 的 `hit_<dir>` 已含方位,向导层可在 60s 内第 2 次同向被撞时把归因句升级为指令句("左边的来了就向左滑"),插入点即 :498-500 的 ids 组装处。
|
||
2. **静默看门狗**:对局是连续流不需要;drill/feature 已有超时。唯一缺口是 ask(W1)——`second_run_ask` 播完后静默,可加提示或超时直接出发。
|
||
3. **结算链补看门狗**:`_gameOver` :573 `_speak`→`_speakGuarded`(守卫里起 `bgm/menu`),照抄 `_escape` :573 的兜底写法。
|
||
4. `_voiceBusy`(:646-648)是项目里最干净的 `_voice` 读取封装,可作向导层判断"此刻能否插播"的基准。
|
||
|
||
---
|
||
|
||
## 6. rpg《剑与声》(web/rpg/game.js,1105 行)
|
||
|
||
### 6.1 状态模型
|
||
- 类 `RpgGame extends Game`(:115)。`phase` 枚举(:118):`idle|ask|intro|explore|shop|battle|menu|over`。**`over` 从未被赋值**(仅 :241 default 注释提及),预留态;`idle` 仅构造后瞬态。
|
||
- 战斗内部二级状态机 `this._battle.state`(:543-561 赋初值):`B_START → PLAYER_SELECT → PLAYER_EXECUTE → CHECK_ENEMY_DEAD → ENEMY_TELEGRAPH → ENEMY_RESOLVE → VICTORY|DEFEAT|FLED|FLEE_FAIL`;另有 `teachStage: 'menu'|'attack'|'parry'|'defend'|null`(四步强制教学 :556)、`tier`(难度档 :559、:567-573)。
|
||
- 其他字段:`scene/optIdx/visited/flags/player/chapters/stats`、`_shopIdx/_menuIdx`、`_ambSrc`(手动环境音循环 :961-982)、`_session`。
|
||
- 流转驱动:**手势 + 语音 onComplete 链 + setTimeout(`_after` :1091)**。探索 `_enterScene`(:251-279)→desc 链→遭遇检查(:282-305)→`_speakOption` 报当前选项;战斗由 `_playerSelect`(:600)与 `_enemyTelegraph`(:770)交替推进。
|
||
|
||
### 6.2 等待输入点清单
|
||
| # | 状态/条件 | 玩家该做什么 | 此刻声音 | 位置 |
|
||
|---|---|---|---|---|
|
||
| W1 | ask:`sys_continue_ask` 播完 | tap=继续存档;right=新游戏。其余忽略 | **静默**(此时未进任何场景音乐,:167-179 无 bgm 调用) | :224-227 |
|
||
| W2 | explore:desc 链播完→当前选项名 `opt_*` 播完 | 左右=切选项(切一个播一个);tap=确认;doubletap=重听 desc+选项;up=状态链;down=菜单 | `amb_*` 常开 + `bgm/village|explore`(:945-949),选项名播完即静默 | `_speakOption` :307-312、`_onExploreGesture` :314-336 |
|
||
| W3 | shop:价格句播完 | 左右=切/tap=买/down=离开/up=状态 | 同上 | :451-464 |
|
||
| W4 | battle `PLAYER_SELECT`:`turn_player` 音 + 650ms 后 `cmd_<指令名>` 播完 | 左右=切指令(切一个播一个);tap=执行;up=敌我血量;down=逃跑 | `bgm/battle|boss`,cmd 播完即静默 | `_playerSelect` :600-612、:654-666 |
|
||
| W5 | battle `ENEMY_TELEGRAPH`:前摇窗口(tier 1.0/1.25/1.5/1.8s + 听风剑 +0.3s) | **doubletap=格挡**(窗口内);其他手势=`error` 震留在窗口 | 前摇音 `tel_*`(Boss 二阶段 1.2 倍速)+ `beat` 震开窗标识 | `_enemyTelegraph` :770-781、:667-678 |
|
||
| W6 | menu | 左右=切(RPG 豁免:菜单与探索同构,文件头 :31-33);tap=确认;up=帮助;down=关闭 | `bgm/menu`(amb 不停,:498) | :509-528 |
|
||
| W7 | intro/over | **不收任何手势**(:241 default) | voice 链 | :241 |
|
||
|
||
- 教学等待点:T0(teachStage='menu',左右切 2 次过关 :658-659)、T1(命中一次 :709-712)、T2(格挡或受击均通过 :819-825)、T3(防御一次 :717-721)——每步都有 8s/16s 看门狗(见 6.6)。
|
||
|
||
### 6.3 播放内容但不接受输入的时刻
|
||
- intro 链(`intro_1..3+skip_hint+sys_new_game`,:198-201):完全不收手势。
|
||
- explore 的 desc/NPC/事件链:tap=跳句(`_voicePlaying()` :324、:1054),**左右切选项/up/down 不被 voice 阻塞**(无全局 gate)——即播 desc 时也能盲切选项(切了会重播 `opt_*` 顶掉 desc)。
|
||
- 战斗:**tap 永不跳语音**(PLAYER_SELECT 中 tap=执行指令 :660-661);文件头 :24-26 声明 `cmd_*` 与 `tut_b*` 不可跳,代码事实上战斗中根本没有跳过路径。
|
||
- 战斗结算链(victory/defeat/ch1_end):`PLAYER_EXECUTE/ENEMY_RESOLVE` 等状态"其他战斗状态:手势忽略"(:679 注释)。
|
||
|
||
### 6.4 错误路径清单
|
||
- 技能无蓝:`btl_nomp` → 4s 后回 PLAYER_SELECT(:688-691);无药:`btl_noitem`(:692-695)。→ 告知型纠错。
|
||
- 逃跑:不可逃战 `error` 震 + `btl_noflee`(:839-844);Lv≥3 逃跑 70%,失败 `btl_flee_fail` → 敌方一个可格挡的前摇回合(:848-856)。
|
||
- **格挡窗口内做错手势:仅 `vibrate('error')`,无语音**(:675-677)——窗口一过就挨打。教学战有 `tut_b2`(:779),非教学战零提示。→ 向导层价值点。
|
||
- 受击:`sfx/hurt` + `hit` 震(:810-811);血量降档:`warning` 震 + `st_hp_low/mid`(:829-832,仅降档才播);战败:`hit`+`error` 震 + `btl_lose+btl_lose_hint` → 回村重生(:886-904)。
|
||
- 格挡成功:`sfx/parry` + `confirm` 震(:672-673,即时回调规避 tap 300ms 延迟)。
|
||
- 纠错性语音:教学战完整(tut_b0..b6 + 看门狗重播);常规战斗靠 `cmd_*` 循环播报即事实上的纠错。
|
||
|
||
### 6.5 已有帮助/状态播报
|
||
- explore/battle/shop up = `_speakStatus`(:431-447):探索 5 句链(lv/hp/gold/weapon/armor,全部分档整句),战斗 2 句(己方+敌方血量档位);血量 low 附 `warning` 震。**全 6 款最完整的状态播报**。
|
||
- menu 帮助 = `help_explore + help_battle + settings_hint`(:521-524)。
|
||
- 战斗回合标点:`sfx/turn_player`(≤0.4s)→650ms 间隙→`cmd_*`(:604-611 注释"听觉标点");前摇开窗 `beat` 震(:778)。
|
||
- 教学看门狗(`_armTeachWatchdog` :630-649):8s 无操作重播一次提示(tut_b0/b3/b6),16s 仍无进展**兜底自动推进**教学步;parry 步不兜底(前摇自然结算)。
|
||
- **探索/常规战斗无卡关计时器**:PLAYER_SELECT 挂机永远等(敌不打你);`_speakGuarded` 全部是语音资产缺失兜底而非卡关逻辑。
|
||
|
||
### 6.6 超时/定时器
|
||
| 定时器 | 时长 | 后果 | 位置 |
|
||
|---|---|---|---|
|
||
| intro 链兜底 | 30s | 进村 | :201 |
|
||
| desc 链兜底 | 20s | 存档→遭遇→报选项 | :278 |
|
||
| 战斗开场链兜底 | 60s | `_playerSelect` | :597 |
|
||
| 教学提示重播 | 8s | 重播 tut_b0/b3/b6 一次 | :634-638 |
|
||
| 教学兜底推进 | 16s | 自动进下一步 | :639-648 |
|
||
| 教学步链兜底 | 8s | `_playerSelect`/`_enemyTelegraph` | :619-622 |
|
||
| nomp/noitem 回合 | 4s | 回 PLAYER_SELECT | :689、:693 |
|
||
| turn_player→cmd | 650ms | 播指令名 | :607 |
|
||
| 敌亡链/连战 | 10s | `_nextEnemy`/`_victory` | :748 |
|
||
| half/phase2 链 | 8s | `_enemyTelegraph` | :759、:763 |
|
||
| 血量降档链 | 5s | 下一回合 | :831 |
|
||
| flee 两分支 | 6s | 结束/前摇 | :852、:855 |
|
||
| victory/defeat/ch1_end | 15s/15s/15s | 收尾 | :883、:893、:936 |
|
||
| 格挡窗口 | 1.0/1.25/1.5/1.8s(+0.3 剑) | 到点 `_enemyResolve` 挨打 | :559、:775-780 |
|
||
|
||
### 6.7 向导层接入点建议
|
||
1. **常规战斗静默看门狗**:`_playerSelect`(:600)挂"教学式"看门狗——非教学战 N 秒无手势重播 `cmd_<当前指令>`(照抄 `_armTeachWatchdog` :630 的 8s 重播模式,去掉 16s 强推)。这是把现成机制推广,改动最小。
|
||
2. **格挡窗口纠错语音**:`_onBattleGesture` :675-677 的 else 分支(现仅 error 震)加节流语音(如 `voice/` "现在是双击")——注意窗口只有 ~1s,语音应走 queue 且短,或仅震动升级(`warning`)。
|
||
3. **轮到你提示**:`turn_player` 标点已是范本;向导层可统一在 `_playerSelect`(:600)后追加播报型钩子。
|
||
4. **状态播报**:`_speakStatus`(:431)即向导层"状态播报"的内容源,接法 = 向导层主动调用即可。
|
||
5. `phase`+`_battle.state` 双层状态完备,向导层可精确知道"该做什么手势"(PLAYER_SELECT=指令循环,TELEGRAPH=doubletap),适合生成通用提示语。
|
||
|
||
---
|
||
|
||
## 7. 全局观察
|
||
|
||
### 7.1 对比表
|
||
|
||
| 维度 | storyline | simon | escape | rhythm | space | rpg |
|
||
|---|---|---|---|---|---|---|
|
||
| **等待输入时的静默处理** | ★☆☆ W1/W2/W3 全静默无信号、无超时;仅 BGM 垫 | ★★★★ input 前 confirm 震+第1轮 your_turn;但 W2 无限静默等 | ★★★☆ W1/W2 静默;PLAY 有 amb 常开可接受 | ★☆☆ main 无任何"轮到你"信号,全靠 BGM 网格自悟;end 链无看门狗 | ★★★☆ 连续流无轮次问题;ask/feature 有超时兜底 | ★★★★★ turn_player 标点+650ms+cmd 播报,是范本 |
|
||
| **错误反馈完整性** | ☆☆☆ 无效手势零反馈(无震无音无语音) | ★★★★ 教学纠错+3次 blunt 升级;对局 retry_use 自动重播 | ★★★★★ wrong/blunt/stuck 三级+nav 带路,全项目最完备 | ★★☆ 教学好(4 类归因+2次跳过);**对局 Miss 仅 error 震无任何语音** | ★★★★ 演练 retry+2次 moveon;对局归因式 protect_*/hit_* | ★★★★ nomp/noitem/noflee/教学看门狗齐全;**格挡窗口错手势仅震动** |
|
||
| **状态可辨识性** | ★★☆ 仅手动 up 查"第几幕";节点级不可知;无效手势无法分辨"没收到" | ★★★★ 每轮整句报数+机会播报+分段成绩;但对局无菜单可退 | ★★★★★ REOPEN/引导/三级卡关+amb/BGM 双层+进展重置,可辨识性最强 | ★★☆ 无对局进度播报;结算链完整;教学结束不可知(挂机停在 tut 静默等) | ★★★★ 保护期宣告/护盾就绪/里程碑/两档警报齐备 | ★★★★★ 四状态 BGM+turn 标点+上滑状态链;phase/state 双层机读友好 |
|
||
| **卡关/静默看门狗** | 无(仅 30s 资产兜底) | 无对局看门狗(最需要) | ★★★ 三级 60/120/180s+L3 循环 | 无(教学靠 _phaseEnd) | 无(feature 8s 除外) | ★★ 教学 8s/16s(非教学无) |
|
||
| **"轮到你"显式提示** | 无 | 震+首轮语音 | 无(开放探索不需) | 无 | 无(连续流不需) | sfx 标点(最好) |
|
||
| **单击跳句 gate 实现** | 自查 promptPlaying | `_tapSkipsVoice` 白名单 | `_voiceSkippable` 封装(最干净) | `_trySkipVoice` | `_voiceBusy` | `_voicePlaying` |
|
||
|
||
### 7.2 结论(谁最好、谁最薄弱)
|
||
|
||
- **错误反馈最完备:escape**(wrong→blunt 升级 + stuck 三级 + nav 带路,且全部语音化);**最薄弱:storyline**(无效手势零反馈——对剧情游戏可辩护,但盲玩家无法分辨"手势没识别"与"这里没这个手势"),其次 **rhythm 对局期**(Miss 无声无语,只有 error 震)与 **rpg 格挡窗口**(1s 决胜窗口内做错只有震动)。
|
||
- **等待输入静默处理最好:rpg**(turn_player+cmd 名构成完整"轮到你+你可选什么"信号链);**最薄弱:rhythm main**(领奏→应答切换零显式信号)与 **storyline W1**(静默即等待);**simon input 是最危险的静默等待**(完全无 BGM、无超时、玩家挂机则永久卡住)。
|
||
- **状态可辨识性最好:escape**(被动三级提示 + 主动 doubletap 兜底播报)与 **rpg**(双层状态 + 状态链);**最薄弱:storyline**(只有幕级播报)。
|
||
- **向导层的四个统一钩子落点**(所有游戏都已具备,接法成本低):
|
||
1. 语音插播通道:各游戏 `_speak`(storyline 用 `playSeq`)+ 引擎 `queue:true`(engine/audio.js:186);
|
||
2. "轮到你"注入点:storyline `onNodeSeqDone`:214 / simon `_toInput`:321 / escape `_advance` done:180 / rhythm `_pump` 首 cue:244 / space `_startRun` 首刷怪前:277 / rpg `_playerSelect`:600;
|
||
3. 静默看门狗模板:escape `_checkStuck`:479(轮询+进展重置+级别集合)与 rpg `_armTeachWatchdog`:630(N 秒重播+2N 兜底推进)两套现成范式,向导层统一实现后逐游戏替换;
|
||
4. 纠错语音注入点:simon :205-215 / escape `_guideWrong`:278 / space `_drillFail`:243 / rhythm `_tutEvaluate`:347 与 `_applyMiss`:548 / rpg :675-677。
|
||
- **引擎侧两件待办**(向导层前置):`audio._voice` 转为公开只读 API(5 处私有读);`skipVoice` 语义与 `_speakGuarded` 看门狗模式可下沉进引擎统一提供。另注意 rhythm `_finish`:679 与 space `_gameOver`:573 的结算链**无看门狗**,语音资产缺失会导致结算 BGM 永不淡入——接入向导层时应一并修复。
|