Files
xsl 3704cd0792 research(platform): 无感用户系统 + PC/手机双端一致 三路调研(身份/键位与语音/数据同步)+ 汇总方案
- 01-identity:推荐自动匿名档案(首访 0 交互)+ Netflix 式切换器 + 配对码跨设备;passkey 因 LAN HTTP 不可用否决;审计出 7/8 游戏跨设备只 PUT 不 GET、大厅零 aria、玩家名不可朗读三个存量问题
- 02-input-parity:方向键/WASD+空格+回车(双击即时)键位(audiogames 社区惯例同构);语音双套资产路线(153 句含操作词,306 TTS/37min);MVP ~200 行 1-2 人日
- 03-data-sync:设备 token 即档案、localStorage 按 profileId 分桶(修同设备串档)、桶级 LWW + baseUpdatedAt 守卫、claim 可选认领 + PIN scrypt;公网前 8 条清单
- 00-synthesis:需求映射 + 架构收敛 + P0/P1 分期 + 3 个待拍板决策点
2026-09-09 23:09:24 +08:00

29 KiB
Raw Permalink Blame History

02 · PC 键盘操作与双端一致体验(Input Parity)调研

2026-09-09 · 交互调研 · 范围:PC 浏览器键盘操作方案、引擎适配、提示(画面+语音)随平台适配、混合设备、测试管线影响。 红线遵守:本文只做调研与设计,不改任何代码。所有数字均从仓库实测/统计得出,来源随文标注。

0. 结论速览

决策点 推荐 一句话理由
键位方案 方向键=四向滑动、空格=单击、回车=双击、按住空格=长按(WASD 作方向别名) 与 A Blind Legend / audiogames.net 社区惯例完全同构,双击走专用键避免把 300ms 延迟带进键盘
语音路线 路线 a:双套资产(voice/<gender>/key/<id> 变体目录,复用 male/female 机制) 与路线 b 重录成本相同(306 文件/37 分钟音频),但增量落地不动已验收资产、两端语气都精确
最小可用(MVP) 引擎键盘通道 + 遮罩键盘解锁 + 画面提示自适应,语音暂不动 200 行改动(gesture.js/audio.js/settings.js/index.html),12 人日;语音资产独立分期

1. 现状审计

1.1 gesture.js(web/engine/gesture.js,323 行):三通道中的两个,无键盘

  • 输入通道:touch 事件(主通道,全 preventDefault 防滚动/缩放/合成鼠标)+ mouse 事件(桌面调试通道,带 700ms recentlyTouched() 守卫防触摸后的合成鼠标误判)。没有任何 keydown/keyup 监听——全仓库 grep keyboard|keydown 零命中。
  • 手势模型:up/down/left/right/tap/doubletap/longpress/longpressend(+immediateTap 模式的 tapcancel)。核心状态机是指针生命周期(down→move→up)+ 长按 450ms 定时器 + 双击 300ms 窗。
  • 关键内部函数:handleDown(x,y) / handleMove(x,y) / handleUp(x,y) / handleCancel()——坐标只用于位移判定(tapSlop/swipeMinDistance),点按与长按判定本身与坐标值无关。这意味着键盘通道可以把"虚拟指针"喂给同一状态机:坐标传元素中心、永不 move,天然只会走出 tap/longpress/doubletap 分支,永远不会误判滑动。
  • attachGestures(element, onGesture, config) 是唯一接线点,10 个游戏 index.html 全部经它接 game.onGesture。键盘层加在 attachGestures 内部 = 游戏代码零改动。
  • immediateTap(rhythm 独占,web/rhythm/index.html:33):按下即发 tap、双击补发改判。键盘空格若复用 handleDown/handleUp,rhythm 的"空格=kick 演奏"自动成立,零改动。
  • 长按旁白快进(1.7x):靠 window 事件 soundgame:longpress/longpressend(audio.js 监听)。键盘长按若复用同一状态机,这两个事件照发,快进零改动。

1.2 启动遮罩(web/engine/audio.js showStartOverlay):只认触摸和鼠标 click

overlay.addEventListener('touchstart', e => e.preventDefault(), {passive:false});
overlay.addEventListener('touchend',   e => { e.preventDefault(); onTap(); }, {passive:false});
overlay.addEventListener('click', onTap);
  • 9 个游戏页 + template 全部传 text: '轻触开始'(audio.js 默认值同)——画面提示的第一个适配点,且键盘玩家目前无法用键盘解锁音频(keydown 不是合法解锁手势路径),必须伸手去摸鼠标。注:浏览器规范里 keydown 属于 user activation 事件,AudioContext.resume() 在 keydown 回调内同步发起即可解锁,技术可行,只是遮罩没监听。
  • 遮罩期间 gesture.js 尚未 attach(index.html 先 await showStartOverlay() 再 attachGestures),所以键盘解锁必须改在 audio.js 里。

1.3 画面提示现状:游戏页几乎无视觉文案,集中在三处

  1. 启动遮罩 hint(audio.js,轻触开始×10 页)
  2. 大厅 footer(web/index.html:209-211):「操作方式:上 / 下 / 左 / 右滑动、单击、双击」
  3. 各游戏 index.html 只有 cover.png 封面 + 遮罩,无其他操作文案——画面提示适配的面积非常小。

1.4 语音提示现状:操作词烘焙在 TTS 资产里(核心约束)

以 tools/gen_voice_dual.py 台词表(8 游戏全量 651 句,TTS 实际数据源)统计含「滑/单击/双击/长按」的句子(regex 实测,2026-09-09):

游戏 台词总数 含操作词句数
storyline 41 21
escape 80 26
simon 78 22
rhythm 40 9
space 59 24
rpg 123 16
radio 103 18
dungeon 127 17
合计 651 153(23.5%)

(注:任务书里「space 27 / dungeon 22 / radio 19」来自对 docs/design/*-voice-ids.md 的表格行 grep;voice-ids 文档只覆盖 5 个游戏且部分行多句,本文以 gen 脚本台词表为准——它是 TTS 管线的真实输入。)

受影响资产实测量(ffprobe 逐文件探测,全部存在):

  • 153 句 × 2 性别 = 306 个音频文件,双格式(.ogg+.m4a)共 612 个文件、49.4 MiB;
  • 音频总时长:male 1078.8s + female 1139.8s = 2218.6s ≈ 37.0 分钟。

双性别资产先例:audio/voice/<gender>/<id>.{ogg,m4a} 整目录切换 + 平铺回退(audio.js _voiceCandidates),设置项 voice(settings.js,枚举校验 + localStorage + 服务器 _settings 同步)——input 变体目录可完整复用这套机制。

1.5 测试管线现状(web/harness/live/)

  • 手势注入:control.mjs /gesture → cdp.mjs Input.dispatchTouchEvent(touchStart/Move/End 序列,swipe 用 8 步插值 200px 位移满足 64px+400px/s 判定)。没有键盘通道。
  • 注入同时打标 __live.events.push({ev:'gesture', type}) 供审计。voice 验收对照 web/harness/voice_text.json(8 游戏期望文本)——语音资产任何措辞变更都要同步此文件,否则 ASR 相似度判定失真。

2. 键位方案(含业界依据)

2.1 业界惯例证据

audiogames.net 社区(盲人音频游戏事实标准社区)——数据库各游戏控制描述呈现出稳定模式:方向键=方位/移动(Night of Parasite「arrow keys represent the four compass directions」、AudioSpeed arrows to steer、Rebound 左右方向键移动);空格=第一主操作(Technoshock space 射击、PacMan Talks space 查距离);回车=菜单确认;"按住"与"连按两下"是既有表达(论坛档案「I: fire your laser, hold down for continuous fire」「F: press twice to reactivate」——audiogames.net 论坛存档)。

A Blind Legend(首批纯音频动作冒险,双端:移动端滑动手势 / PC Steam 键盘)——Steam 社区键位参考:←/→ 旋转、↑ 前进(按住持续)、空格呼叫向导、Esc 菜单、回车确认。它是"同一游戏、触屏手势与键盘两套措辞并行"的直接先例(移动端教学说的是手势,PC 版说的是按键)。

Blind Drive(音频游戏奖项得主,双端)——官网自称「2-button arcade gameplay」,PC 版键盘(+手柄)只做左右闪避(Steam 页、评测)——证明音频游戏在 PC 上会把触屏/体感操作压缩成极简键盘映射。

结论:音频游戏键盘惯例 = 方向键表方位、空格表主操作、回车表确认/第二操作、按住表持续。SoundGame 的 6 手势可以无损映射进这个惯例。

2.2 推荐键位表(主方案)

手势(触屏语义) 键盘主键 备用 说明
左滑 / 右滑 ← / → A / D keydown 即发,零延迟(无距离/速度判定)
上滑 / 下滑 ↑ / ↓ W / S 同上
单击 空格 — 走现有 tap 状态机:快速点按=tap(保留 ~300ms 双击窗延迟,与触屏同感);immediateTap 游戏(rhythm)按下即响
双击 回车 空格连按两下 专用键即时触发(不等 300ms 窗);空格×2 走状态机也能判出 doubletap(第一次的 pending tap 被取消,行为与触屏双击完全一致)
长按 按住空格 ≥450ms — 松开=longpressend;voice 播放中即旁白 1.7x 快进(复用 window 事件)

设计要点与理由:

  1. 双击给专用键(回车)而不是只靠连按空格——这是本项目最重要的键位决策。若键盘只允许"空格×2",单击就必须等 300ms 双击窗(gesture.js 文件头自己写着这是要避开的坑)。回车=双击让"确认类"操作即时响应(A Blind Legend 回车=confirm 同构);同时保留空格×2 兼容,触屏肌肉记忆用户无学习成本。
  2. 长按复用空格而非另设键:长按的语义就是"按住那个确认键",且 audio.js 快进通道长按期间持续生效,独立键反而多一个要教的键位。
  3. WASD 作别名默认开启:单手党/游戏手玩家习惯;不与箭头冲突,成本为零。
  4. 不映射 Esc(下滑已覆盖菜单;Esc 留给未来退出全屏),不占 F 键/数字键(避开浏览器与读屏快捷键域)。

2.3 键位冲突分析

  • 浏览器默认行为:箭头键与空格会滚动页面、回车在焦点元素上触发 click——全部在 keydown 里 preventDefault() 解决(页面本就 overflow:hidden + touch-action:none,见 style.css;preventDefault 是双保险,MDN keyboard-navigable widgets 的标准做法)。
  • 表单目标守卫:游戏页无输入框,但大厅有(用户名/PIN <input>)——键盘层遇 INPUT/TEXTAREA/SELECT/contenteditable 目标直接放行不拦截,避免大厅打不了字。
  • 读屏软件:NVDA/争渡在"浏览模式"会先吃掉箭头键(Stack Overflow: Screen Reader Blocks Keyboard Events)。本项目游戏页无 DOM 文本可浏览,惯例解法是遮罩/根元素加 role="application" 促使读屏直通按键——列为实现时的验证项,不是阻塞项(本项目视障玩家主要在手机端,PC 视障玩家属加分场景)。
  • 与游戏内快捷键:现有游戏无键盘快捷键,无冲突。

3. 引擎适配设计(文件级)

3.1 总体思路:并行通道,不做先抽象后重写

在 attachGestures() 内并列增加键盘通道,与 touch/mouse 共享同一状态机与回调——不做"统一 InputChannel 抽象层"(323 行引擎重写风险大、收益为零;现有三通道已经证明并列式可行)。

3.2 gesture.js:新增键盘通道(预计 ~120 行)

attachGestures(element, onGesture, config) 内新增:

KEY_MAP = { ArrowUp:'up', ArrowDown:'down', ArrowLeft:'left', ArrowRight:'right',
            KeyA:'left', KeyD:'right', KeyW:'up', KeyS:'down' }   // WASD 别名
TAP_KEY = 'Space'; DBL_KEY = 'Enter';

window.addEventListener('keydown', onKeyDown) / 'keyup', onKeyUp)   // detach 时移除

onKeyDown(e):
  1. e.repeat → return                        // 方向键 repeat 抑制(按住↑只发一次 up)
  2. e.target 是表单元素 → return              // 大厅输入框放行
  3. 仅拦截映射键;其余键不动                   // 不吞 Tab(无障碍:别做键盘陷阱)
  4. e.preventDefault()                        // 抑制滚动/焦点激活
  5. tracking 时 return                        // 单飞行指针语义(与现有多指忽略一致,
                                               //  见 onTouchStart 的 e.touches.length>1 分支)
  6. 方向键 → vibrate('swipe'); onGesture(dir)          // 即时,无位移判定
     回车     → vibrate('confirm'); onGesture('doubletap') // 即时,同时 cancelPendingTap()
     空格     → handleDown(centerX, centerY)             // 进状态机:起 450ms 长按定时器;
                                                        // immediateTap 游戏立即发 tap
onKeyUp(e):
  空格 → handleUp(centerX, centerY)   // 复用收尾:长按→longpressend;快按→tap/双击窗
                                       // (连按两下空格自然判 doubletap)

实现细节:

  • 坐标取元素中心(getBoundingClientRect() 缓存),永不 move——键盘路径必然只走 tap/longpress/doubletap 分支。
  • 450ms 长按判定:完全复用 handleDown 里的 longPressTimer,无新逻辑。
  • 300ms 双击窗:完全复用 lastTapTime/pendingTapTimer,键盘空格与触摸 tap 共享窗口(混合设备下"触屏点一下+空格点一下"也会判双击——语义正确:同一虚拟指针)。
  • 互斥:tracking 标志天然互斥(touch start 或 Space down 先到先得),与现有多指忽略语义一致,不需要 per-channel 状态。
  • 焦点:键盘事件挂 window(键盘事件目标是焦点元素而非手势根;游戏页焦点在 body,冒泡到 window 必达)。
  • 通道来源标记(可选但建议):onGesture 前记 lastInputChannel='key'|'touch'|'mouse' 并导出 getActiveChannel()——§4 提示自适应与 §6 测试都要用。审计事件 soundgame:input(CustomEvent)亦可,供 harness 打标。

3.3 audio.js:showStartOverlay 键盘解锁 + 自适应文案(~20 行)

// 键盘解锁:回车/空格(keydown 属 user activation,同步发起 resume 即可)
const onKey = (e) => { if (e.code === 'Enter' || e.code === 'Space') { e.preventDefault(); onTap(); } };
overlay.addEventListener('keydown', onKey);
// (overlay 需 tabindex="0" 并 focus(),keydown 才会落在它上面;或直接挂 window,
//   resolve 后移除监听)
  • 文案自适应:text 参数缺省时按 isTouchLikely()(§4.1)选「轻触开始」/「按回车或点击开始」;显式传 text 的调用照旧(全部 10 页现在都显式传 '轻触开始'——改成不传即自动,属可选的各页 1 行清理,不传也能跑)。

3.4 settings.js + 大厅:新增 input 设置(~25 行)

  • input: 'auto' | 'touch' | 'key'(默认 auto),进 VALID 枚举;_settings 服务器同步机制零改动自动带上。
  • 大厅设置面板加一个 <select>(自动 / 触屏优先 / 键盘优先)。
  • 语义:auto = 三通道全开 + 提示跟随最近使用的通道;touch/key 只影响提示文案与语音变体选择,不关闭另一通道(永远双通道在线,混合设备友好,见 §5)。
  • footer 文案随 input/检测切换。

3.5 游戏代码改动边界

项 改动
8 个游戏 game.js 的 onGesture 逻辑 零改动(7 种事件语义不变)
rhythm immediateTap 零改动(空格按下即 kick,长按=双通道一致)
旁白长按快进 零改动(window 事件照发)
游戏页 index.html 可选各 1 行(遮罩 text 改自适应);不改也能跑
gesture.js / audio.js / settings.js / 大厅 index.html 引擎侧统一改(上述 ~185 行)

4. 提示适配机制

4.1 平台检测:能力检测 + 运行时跟随,不做一次性硬判

依据:MDN @media/pointer、@media/any-pointer、web.dev Interaction、CSS-Tricks 对混合设备的警告。

  • 初始值:isTouchLikely() = navigator.maxTouchPoints > 0 && matchMedia('(pointer: coarse)').matches——触屏手机=true;纯桌面=false;触屏笔记本(pointer: fine 主指针 + 有触屏)=false(初始按键鼠提示,运行时纠正)。
  • 运行时跟随:gesture.js 记录 lastInputChannel;收到键盘事件后本会话提示切到键盘版,收到触摸切回。混合设备无需玩家配置即可用对提示(settings.input 手动覆盖只是兜底)。
  • 手动覆盖:settings.input(§3.4)。

4.2 画面提示文案表

位置 触屏版 键盘版
启动遮罩(audio.js) 轻触开始 按回车或单击开始
大厅 footer(index.html) 操作方式:上 / 下 / 左 / 右滑动、单击、双击 操作方式:方向键移动、空格单击、回车双击、按住空格长按
手势教学文案(游戏内 HTML,目前无) 向左滑 按 ← 键
大厅 footer 第二行(300ms 延迟说明) 保留 键盘版改为「回车=双击即时;空格单击有约 300ms 延迟(与手机一致)」

实现:一个 inputHints.js 小工具(~40 行)导出 hintTable() 按当前通道返回文案对象,audio.js 与大厅共用;CSS 侧如需图标/布局差异走 @media (pointer: coarse)。

4.3 语音提示三路线对比(核心决策)

先回答调研问题:视障用户在 PC 上听「向左滑」会困惑吗? 会,但程度有限且不该依赖它。audiogames.net 社区是 PC 键盘文化的大本营(键盘是默认、触屏是移动端补充);盲人社区对"手势↔键盘等价命令"的映射有明确预期——AppleVis: Braille Display Users Deserve Better From Blindfold Games 明确批评过"只在触屏上讲手势、不给键盘等价命令"的做法。NVDA 用户知道触屏手势存在键盘等价物这个概念,但游戏语音说「向左滑」时,键盘玩家必须自己完成一次"向左滑≈按←"的翻译——首次听不懂、需要猜,违背本项目「开场交互教学」理念(教学句本身就在这 153 句里,如 storyline help 全句是操作说明)。学术侧同样有「默认键位不合用户习惯是音频游戏主要可用性问题」的记录(Design Guidelines for Audio Games)。路线 c 不能作终态。

路线 a:双套语音资产(input 变体目录,像双性别一样)

机制:voice/<gender>/key/<id>.{ogg,m4a},audio.js _voiceCandidates 追加一跳(key 目录 → gender 目录 → 平铺回退),id 集合只需覆盖 153 句(其余回退现有资产,不存在双份)。

维度 评估
资产成本 新增 153 句 × 2 性别 = 306 个 TTS 生成(措辞改写:「向左滑」→「按左方向键」、「单击屏幕」→「按一下空格」、「双击」→「按回车」);落盘 612 文件(ogg+m4a)、+49.4 MiB 仓库增量;新音频 37.0 分钟(实测同 id 现有资产时长,措辞微调后误差 <10%)
GPU 时长 Qwen3-TTS 本地串行(flock)。参照系:R1 全量双性别管线 286 文件/43.5 分钟音频一个晚间会话完成(docs/audio/round1-report.md);本路线 306 文件/37 分钟与之同量级——单晚 GPU 串行一轮 + --check 复检 + 少量离群重跑,≤ 半个 GPU 晚。管线零改动支持:台词表加 (fid, instruct, text, input='key') 维度或直接新增 *_KEY_LINES 表,断点续跑/--only/--check/--convert 全部照用
体验 最优:两端教学语气精确(「单击屏幕,继续剧情」→「按一下空格,继续剧情」,指令动词与输入设备严格一致)
风险 ①153 句键盘措辞要策划逐句改写+试听(与 b 相同);②台词表长期 ×2 维护(改一句要过两个变体);③voice_text.json 需按 input 变体分集(live 管线 session 加 input 字段选期望文本集);④旧资产不动、回退安全,增量落地可灰度

路线 b:平台无关措辞重录(一套通吃)

机制:153 句全部改成不含输入方式的措辞(「向左滑:先检查收音机」→「向左:先检查收音机」),替换现有 306 文件。

维度 评估
资产成本 同样 306 个 TTS 生成(替换而非新增),37 分钟音频重录,仓库体积持平(措辞略短或 -5%);GPU 时长与 a 完全相同
额外成本 ①已验收资产全部作废重验(8 游戏 R1~R6 轮次里这 153 句的试听结论失效);②voice_text.json 153 句期望文本全量重对齐;③逐句审查"去操作词后语义仍完整"(如 storyline e2「单击屏幕,重新开始」→「单击,重新开始?」教学力度弱化)
体验 两端通用但两端都变抽象:触屏新手失去"滑"这个动词(本项目核心新手群体恰是手机端),键盘玩家仍要做一次"单击=空格"的映射(单击一词对键盘玩家仍非按键名)。属于两端各让一步
风险 替换式落地无法灰度;ASR 对照/多音字清单/验收记录全要重跑

路线 c:语音不动,PC 靠画面文字提示

维度 评估
成本 零 TTS、零资产改动;只有 §4.2 的画面文案工作
体验 纯音频游戏玩家不看画面(本项目立项前提),PC 视障玩家听到的仍是「向左滑」,见上文社区证据——键盘等价命令缺失是被盲人社区明确批评过的可及性缺陷
定位 只允许作为 MVP 过渡态(PC 先上线、语音资产下一轮补),且要在 PC 版开场遮罩/大厅用文字+后续一句通用语音说明「键盘操作:方向键/空格/回车」兜底

三路线速比

a 双套 b 重录通用 c 不动语音
TTS 生成量 306 文件 / 37min 306 文件 / 37min 0
仓库增量 +49.4 MiB ~0 0
策划文案工作 153 句新写 153 句改写+全量重审 0
触屏端体验 不变(原资产不动) 轻微弱化 不变
PC 端体验 最优 中 差(视障不可用级)
落地方式 增量可灰度 替换不可灰度 —
长期维护 台词表 ×2 台词表 ×1 ×1(欠账)

推荐 a:与 b 成本几乎相同但风险低一档(不动已验收资产、可灰度、触屏端零感知),且两端教学语气都保住精确性;机制上完整复用 male/female 双性别目录的成熟先例(引擎解析、管线、QC、设置项全套现成)。


5. 混合设备(触屏笔记本 / 平板外接键盘)

  • 运行策略:三通道永远并行在线,不做模式切换。 gesture.js 的 tracking 单飞行指针语义天然防跨通道干扰(触摸进行中空格 down 被忽略,反之亦然——与现有多指忽略一致)。
  • 提示跟随最近通道(§4.1 运行时跟随):玩家把平板接上键盘敲了一下方向键,遮罩外的教学提示文案本会话即切键盘版;摸了屏又切回。无需重启页面、无需设置项。
  • 语音变体(路线 a 落地后)同理跟随 lastInputChannel;settings.input='auto' 之外的手动值仅用于"我知道我在用什么,别自动切"的用户。
  • 一个已知边界:混合设备上"触摸单击 + 键盘空格单击"共享同一个双击窗(同一虚拟指针),300ms 内跨通道两下会判 doubletap——语义上合理(玩家意图就是双击),文档写明即可,不做隔离。

6. 测试影响(playtest / live 管线)

  • live 管线加键盘通道(建议,~30 行):control.mjs /gesture 接受 {"type":"keyup","code":"ArrowLeft"} 之类或新增 /key 端点 → cdp.mjs 增加 Input.dispatchKeyEvent(rawKeyDown/keyUp;比 Runtime.evaluate 派发合成 KeyboardEvent 更拟真——不过 gesture 层挂 window、合成事件也够用,CDP 通道留作高拟真档)。审计事件沿用 {ev:'gesture', type} 打标,附 source:'key'。
  • 解锁路径必须测键盘版:会话第一步 tap 解锁遮罩(现状 README 明确"第一个动作必须是 tap");键盘通道上线后补一条"第一个动作 = Enter 解锁"的用例,验证 keydown 路径的 AudioContext resume。
  • 双通道回归:现有触摸用例零改动可跑(键盘层不改变触摸路径);新增少量键盘用例重点覆盖:方向键 repeat 抑制(按住 1s 只发一次)、空格 300ms 双击窗(快按两下=doubletap、慢按=tap)、按住 450ms=longpress+旁白快进(__live 的 boost 事件)、大厅输入框打字不被拦截。
  • voice_text.json:路线 a 落地时按 input 变体分集(session settings 已支持传 voice/speed/bgm/guide,加 input 字段即可);路线 b 则要全量重对齐——这也是推荐 a 的测试侧理由。
  • playtest 脚本(sessions/):若某轮 playtest 主题是"PC 玩家体验",直接用键盘通道跑既有剧本即可,无需新 harness。

7. 推荐组合与分期

MVP(最小可用,PC 能完整玩全部 8 游戏)——约 1~2 人日

改动 文件 量级
键盘输入通道(含 repeat 抑制/表单守卫/滚动抑制/通道标记) web/engine/gesture.js ~120 行
遮罩键盘解锁 + 自适应默认文案 web/engine/audio.js ~20 行
input 设置项(auto/touch/key) web/engine/settings.js + web/index.html ~25 行
大厅 footer 文案切换 + inputHints 工具 web/index.html(+新 inputHints.js ~40 行) ~45 行
live 管线键盘通道 + 解锁/双通道用例 web/harness/live/{control,cdp}.mjs ~30 行

语音此时不动(路线 c 过渡态):PC 遮罩文字提示写明「键盘:方向键/空格/回车/按住空格」,教学语音里的「向左滑」暂由玩家自行映射——过渡期建议不超过一个语音资产轮次。

完整版(语音双端精确)——策划 153 句键盘措辞 + 半个 GPU 晚 + 工程半日

  1. 策划:153 句键盘版措辞改写(台词表新增 key 变体行,参照 15/16 号 voice-ids 文档格式交付);
  2. 资产:gen_voice_dual.py 加变体维度,--game all 串行一轮(306 生成/612 落盘/+49.4 MiB),--check 复检;
  3. 工程:audio.js voice 解析加 key 目录跳(~15 行)、harness voice_text.json 分集(~20 行);
  4. 验收:live 管线 input:'key' 会话跑 8 游戏开场教学段,ASR 对照键盘版文本。

依赖与顺序

MVP 与语音路线完全解耦,可先行;完整版的变体目录机制依赖 MVP 的 lastInputChannel(决定加载哪套语音)。混合设备体验在 MVP 即达标(双通道并行+提示跟随),完整版补齐语音一致性。


附:本文数字复现方式

  • 台词/操作词统计:tools/gen_voice_dual.py 各 *_LINES 表,regex [滑|单击|双击|长按](2026-09-09 实测 153/651 句);
  • 资产时长/体积:对受影响 306 个 .ogg ffprobe 求和(male 1078.8s / female 1139.8s),文件级 du 合计 49.4 MiB(612 文件含 .m4a);
  • 业界链接均已在 §2/§4 正文内嵌。

附:业界依据来源