- 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 个待拍板决策点
29 KiB
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) | 引擎键盘通道 + 遮罩键盘解锁 + 画面提示自适应,语音暂不动 |
1. 现状审计
1.1 gesture.js(web/engine/gesture.js,323 行):三通道中的两个,无键盘
- 输入通道:touch 事件(主通道,全 preventDefault 防滚动/缩放/合成鼠标)+ mouse 事件(桌面调试通道,带 700ms
recentlyTouched()守卫防触摸后的合成鼠标误判)。没有任何 keydown/keyup 监听——全仓库 grepkeyboard|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 画面提示现状:游戏页几乎无视觉文案,集中在三处
- 启动遮罩 hint(audio.js,
轻触开始×10 页) - 大厅 footer(web/index.html:209-211):「操作方式:上 / 下 / 左 / 右滑动、单击、双击」
- 各游戏 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.mjsInput.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 事件) |
设计要点与理由:
- 双击给专用键(回车)而不是只靠连按空格——这是本项目最重要的键位决策。若键盘只允许"空格×2",单击就必须等 300ms 双击窗(gesture.js 文件头自己写着这是要避开的坑)。回车=双击让"确认类"操作即时响应(A Blind Legend 回车=confirm 同构);同时保留空格×2 兼容,触屏肌肉记忆用户无学习成本。
- 长按复用空格而非另设键:长按的语义就是"按住那个确认键",且 audio.js 快进通道长按期间持续生效,独立键反而多一个要教的键位。
- WASD 作别名默认开启:单手党/游戏手玩家习惯;不与箭头冲突,成本为零。
- 不映射 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 晚 + 工程半日
- 策划:153 句键盘版措辞改写(台词表新增 key 变体行,参照 15/16 号 voice-ids 文档格式交付);
- 资产:gen_voice_dual.py 加变体维度,
--game all串行一轮(306 生成/612 落盘/+49.4 MiB),--check复检; - 工程:audio.js voice 解析加 key 目录跳(~15 行)、harness voice_text.json 分集(~20 行);
- 验收: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 正文内嵌。
附:业界依据来源
- A Blind Legend — Steam 社区键位参考、Steam 商店页
- Blind Drive 官网、Steam 页、Traxion 评测、PCGamingWiki
- audiogames.net 数据库:Night of Parasite、Technoshock、PacMan Talks、Rebound、AudioSpeed、论坛存档
- AppleVis — Braille Display Users Deserve Better From Blindfold Games(手势↔键盘等价命令)
- MDN — @media/pointer、@media/any-pointer、Keyboard-navigable JavaScript widgets
- web.dev — Interaction、CSS-Tricks — Interaction Media Features 混合设备陷阱
- Stack Overflow — Screen Reader Blocks Keyboard Events
- Design Guidelines for Audio Games