Files
SoundGame/research/03-schemes/game-inventory.md
T
SoundGame Dev 18f299f446 research: 交互研究项目立项(章程/5路调研4份/游戏交互清点/玩家感知转写harness)
- 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解析)
2026-08-30 21:10:06 +08:00

41 KiB
Raw Blame History

六款游戏交互接线清单(「向导交互层」接入前置调研)

调研方法:逐行阅读 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),选项名播完即静默
W3 shop:价格句播完 左右=切/tap=买/down=离开/up=状态 同上 :451-464
W4 battle PLAYER_SELECT:turn_player 音 + 650ms 后 cmd_<指令名> 播完 左右=切指令(切一个播一个);tap=执行;up=敌我血量;down=逃跑 `bgm/battle boss`,cmd 播完即静默
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 永不淡入——接入向导层时应一并修复。