- 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解析)
41 KiB
41 KiB
六款游戏交互接线清单(「向导交互层」接入前置调研)
调研方法:逐行阅读
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、e4sfx/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 向导层接入点建议
- 状态播报:向导层劫持
playSeq(:230)即可掌握全部旁白时机;"你现在的进度"语音可在onNodeSeqDone(:214)后追加(在 voice 链尾部排队,不与 p* 提示冲突——p* 是链的末句,播完即 W2,向导层应避免在此追加,见下)。 - 轮到你提示:W1(叙事节点播完)在
onNodeSeqDone:226 分支插"轻触继续"提示(现状完全静默,是全 6 款中最盲的等待点)。W2 不可插语音(会破坏"提示音播完即可选"的契约),可改用震动(vibrate('confirm'))标记choiceArmed=true(:217)。 - 静默看门狗:W1/W2/W3 均无超时。建议在
onNodeSeqDone(:214)挂 N 秒无手势→重播voice/help+status(复用playHelpAndResume:284)。W3 同理(awaitingRestart置位点 :224)。 - 纠错反馈:
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(答对/答错后的播报链,_speakGuarded10s):忽略;播完自动_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 向导层接入点建议
- 静默看门狗(最优先):
_toInput(:321)挂计时——N 秒无输入先播提示再自动重听(复用_relisten:344 逻辑但可免机会扣减或改为无限次提示);gameover链播完(:413_speak(ids, null)的 null 回调处)同理。 - 轮到你提示泛化:
_playSequence:304 的confirm震 + :306your_turn(仅第 1 轮)——向导层把"轮到你"语音做成低打扰版(震型升级或每 K 轮播一次)只需改这两行。 - 状态播报:
phase字段完备,向导层可直接读;劫持_speak(:502)即可统一插播。 - 菜单缺口:对局中(input/playback)无菜单呼出路径,唯一退出是故意答错到 gameover——向导层或需一个不与 6 手势冲突的呼出方案(长按已全局占用为快进,engine/gesture.js:14-19)。
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 L1stuck_ask+stuck_<stage>_1(暗示);120s L2stuck_<stage>_2(明示);180s L3stuck_<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 向导层接入点建议
- 三级卡关即现成的静默看门狗骨架:向导层接管
_checkStuck(:479)或把阈值/级别改由向导层配置;_progressEvent(:475)就是标准的"重置看门狗"钩子。 - 轮到你提示:W1/W2 播完即静默,可在
_advancedone(:180-187)和 REOPEN(:126)后加轻提示;PLAY 是开放探索不需要。 - 帮助缺口:PLAY 无帮助手势——向导层的"状态播报"可挂到向导专属入口(如连续 N 次撞墙后主动播
nav),不必新增手势。 _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 向导层接入点建议
- 对局纠错语音:
_applyMiss(:548)加连续 N 次 Miss 触发一次voice/提示(素材可参考教学retry_gesture);插播通道建议复用play('voice/…', {queue:true})(engine/audio.js:186)避免顶掉 mid_song。 - 轮到你提示:在泵里检测"首个未判定 cue 进入窗口"(
_pump:244 附近可取_cues首个!judged),首乐句播一次your_turn型提示;或谱面排程时向_timed推入(:414-418 旁已有 mid_song 先例)。 - 结算链补看门狗:
_finish:679_speak→_speakGuarded(守卫里起bgm/menu)。 - 状态播报: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 向导层接入点建议
- 死亡教学升级:
_resolve:492-501 的hit_<dir>已含方位,向导层可在 60s 内第 2 次同向被撞时把归因句升级为指令句("左边的来了就向左滑"),插入点即 :498-500 的 ids 组装处。 - 静默看门狗:对局是连续流不需要;drill/feature 已有超时。唯一缺口是 ask(W1)——
second_run_ask播完后静默,可加提示或超时直接出发。 - 结算链补看门狗:
_gameOver:573_speak→_speakGuarded(守卫里起bgm/menu),照抄_escape:573 的兜底写法。 _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 向导层接入点建议
- 常规战斗静默看门狗:
_playerSelect(:600)挂"教学式"看门狗——非教学战 N 秒无手势重播cmd_<当前指令>(照抄_armTeachWatchdog:630 的 8s 重播模式,去掉 16s 强推)。这是把现成机制推广,改动最小。 - 格挡窗口纠错语音:
_onBattleGesture:675-677 的 else 分支(现仅 error 震)加节流语音(如voice/"现在是双击")——注意窗口只有 ~1s,语音应走 queue 且短,或仅震动升级(warning)。 - 轮到你提示:
turn_player标点已是范本;向导层可统一在_playerSelect(:600)后追加播报型钩子。 - 状态播报:
_speakStatus(:431)即向导层"状态播报"的内容源,接法 = 向导层主动调用即可。 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(只有幕级播报)。
- 向导层的四个统一钩子落点(所有游戏都已具备,接法成本低):
- 语音插播通道:各游戏
_speak(storyline 用playSeq)+ 引擎queue:true(engine/audio.js:186); - "轮到你"注入点:storyline
onNodeSeqDone:214 / simon_toInput:321 / escape_advancedone:180 / rhythm_pump首 cue:244 / space_startRun首刷怪前:277 / rpg_playerSelect:600; - 静默看门狗模板:escape
_checkStuck:479(轮询+进展重置+级别集合)与 rpg_armTeachWatchdog:630(N 秒重播+2N 兜底推进)两套现成范式,向导层统一实现后逐游戏替换; - 纠错语音注入点: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 永不淡入——接入向导层时应一并修复。