Files
SoundGame/docs/design/04-space-rebuild.md
T
xsl ddada6980c fix(space): 盲航刺耳音效重制 + 状态/反馈盲区修复(玩家代理取证迭代)
- F1 程序合成重制5件刺耳音效(alarm≥3kHz能量82%→0,全件≤-3.4dBFS)+新增挥空音
- F2 P0:撞击方位语音防顶断补播(保护期结束死亡窗 hit_* 零毫秒被杀根因)
- F3 保护期教学句7连互截去重;F4 新句 protect_soon 50s预告;F5 boost_not_ready 冷却拒绝回音;F6 激活补CONFIRM震;F7 whiff
- 验证:playtest 172/172 + guide 122 + 终检全绿 + 14游戏回归零回退 + live两轮复跑坐实
- 档案 research/space-polish/round1-2(取证+修复证据);04号文档追加R5修订记录
2026-09-14 20:16:58 +08:00

158 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 04 · 《盲航》重构方案(space rebuild)
> **给开发者的实现要点摘要**
> 1. 47 秒连续旁白教学(intro1-4 链播)改为**交互式演练**:每次只教一件事,玩家立刻实操一颗慢速陨石,做错不惩罚、当场重教。四个方向各练一次,单击/双击在开局后第 20s / 45s 分批引入。
> 2. **闪避方向改为同向**:听到左边的呼啸就向左滑——"把陨石拨开"。反向映射(现版左来右滑)是"没搞懂就死了"的最大认知负担,同向映射让方位声直接映射到同侧手势。
> 3. **前 60 秒保护期**:陨石慢(逼近 4s)、稀(间隔 4s)、撞了不掉血(训练护盾吸收,语音解释)。60s 后语音宣告"保护解除",难度曲线从现版起点再降一档重新爬坡。
> 4. **死亡即教学**:每次被撞,语音说清陨石从哪来、该怎么做;坠毁结算播报分因果。
> 5. 数字拼接全部改整句:里程碑 5 句固定整句,存活时间分 4 段整句。撞击/护盾等反馈改震动。
---
## 1. 现版诊断:"没搞懂就死了"
读 `web/space/game.js`:
1. **教学是 4 句连播旁白**(`voice/intro1..4`, game.js:84-87),约 47 秒。玩家零操作听完,然后第一颗陨石 1.2 秒后就来(`_scheduleSpawn(1200)`, game.js:111)。方位声→反向手势的映射只在旁白里说过一遍,没有任何一次实操验证。
2. **反向闪避违反直觉**(`ESCAPE_SWIPE = {left:'right',...}`, game.js:26)。声音从左边来,手却要往右滑——玩家在前几秒的生存窗口里要完成"定位→取反→出手"三步推理,取反这一步是死亡率主因。
3. **三个机制同时压上来**:闪避、单击加速、双击护盾,47 秒旁白里全部讲完,开局全部可用。玩家还没分清 whoosh 的方位,护盾冷却的播报已经来了。
4. **难度起点过高**:首颗逼近时长 2.8s、刷怪间隔 3.2s(game.js:42-44),对没建立映射的新手等于即死。
5. **撞击无教学价值**:`voice/hit1/hit2` 只报损,不说陨石从哪来、该怎么躲。玩家死了都不知道为什么。
## 2. 核心决策:同向闪避
**听到哪边来,就向哪边滑**(`PUSH_SWIPE = {left:'left', right:'right', up:'up', down:'down'}`),叙事包装为"挥手把陨石拨开/推引擎避开"。
- 方位声(左声道)→ 同侧手势(左滑):零推理,反射直达,这是盲玩能成立的前提。
- 上=高频呼啸→上滑,下=低频轰隆→下滑,保留现有 sfx 方位设计(whoosh_* 四个资产全部复用,只改映射表和文案)。
- 判定窗口沿用现版结构(撞击前 1.5s 内滑动有效 + 250ms 宽限),仅 `need` 从 `ESCAPE_SWIPE[dir]` 改为 `dir` 本身。
- 单击加速、双击护盾机制保留(现版"持续增益规避 tap 延迟"的设计是对的,game.js:146-153 注释写得很清楚),只是引入时机后移(§3.3)。
## 3. 新开场:交互式演练(DRILL 阶段)
### 3.1 演练流程
每步 = 一句短旁白 → 一颗**演练陨石**(逼近 4.5s,撞了不掉血)→ 玩家操作 → 反馈。做错/超时:ERROR 震动 + 一句纠正 + 同一颗重来,**最多重教 2 次后自动演示通过**(不卡流程)。
| 步 | 旁白(整句,≤20字/句) | 演练内容 |
|---|---|---|
| D0 | "欢迎登上盲航。你只有一双耳朵。" + "陨石来了,会有声音告诉你方位。" | 无陨石,建立预期 |
| D1 | "听,左边有陨石。向左滑,把它拨开!" | 左来,需左滑 |
| D2 | "这次在右边。" | 右来,需右滑 |
| D3 | "上面有东西,声音又尖又高。向上滑!" | 上来,需上滑 |
| D4 | "下面是低频的轰隆。向下滑!" | 下来,需下滑 |
| D5 | "四个方向你都会了。引擎点火,出发!" | 进保护期 |
- 每次成功拨开:dodge 音 + SWIPE_OK 震动 + 短肯定("漂亮。"仅 D1/D3 播,避免啰嗦)。
- 每步旁白在**演练陨石的 whoosh 响起之前**播完(语音先、音效后,不叠放),whoosh 响起时玩家已知该做什么。
- 教学期间下滑=跳过演练(二周目直接问"上滑重听演练,单击出发")。
### 3.2 前 60 秒保护期
演练结束进入 `playing` 后启动保护期(`protectionUntil = runStart + 60s`):
| 参数 | 保护期 | 保护期后 |
|---|---|---|
| 逼近时长 | 4.0s(恒定) | 3.2s → 2.0s 爬坡(比现版 2.8→1.8 各抬 0.4s) |
| 刷怪间隔 | 4.0s(恒定) | 3.2s → 1.4s 爬坡 |
| 撞击 | 不掉血:训练护盾吸收,播 shield_block 音 + WARNING 震动 + 语音解释("护盾接住了。那颗在左边,听到左声道的呼啸就向左滑。"按实际方位选句) | 正常掉血 |
| 单击/双击 | 第 20s / 45s 才解锁(见 §3.3) | 可用 |
第 60s 播:"训练护盾关闭,接下来靠你自己了。" + WARNING 震动。之后难度从低档开始 120s 爬满(沿用 RAMP_MS=120s)。
### 3.3 单击/双击分批引入
- **第 20 秒**(保护期内):暂停刷怪 6 秒,旁白:"试试单击,引擎会短促加速,直接冲过空隙。" → 玩家单击 → boost 音 + CONFIRM 震动 + "感觉到了吗?加速期间撞不上你。" → 恢复刷怪。
- **第 45 秒**:同样暂停 6 秒:"双击展开护盾,能挡一次撞击,用完要 30 秒充能。" → 玩家双击 → shield 音 + CONFIRM 震动 → 恢复。
- 若玩家 8 秒内没做,不等待直接继续(机制仍在,之后用上也正常生效)。
- 护盾充能完毕语音(`voice/shield_ready`)保留。
## 4. 死亡即教学 + 结算
### 4.1 撞击反馈(保护期后)
每次被撞:`sfx/hit` + HIT 震动 + 一句归因语音(按方位 4 句预生成:"左边撞上的。听到左声道呼啸,向左滑。"),替换现版 `voice/hit1/hit2` 的纯报损。第二次被撞额外加半句船体状态:"船体只剩最后一次了。"
### 4.2 坠毁结算(整句化)
删除 `numClips` 拼接体系(`voice/num*`/`n_time`/`n_sec`/`n_dodge`/`n_ge`, game.js:47-65):
- **存活时间** 4 段整句:"不到三十秒,再试一次。""超过三十秒,有进步。""超过一分钟,合格的飞行员。""超过两分钟,王牌飞行员。"
- **躲避数** 4 段整句(合并进上句的第二句):"只拨开几颗。""拨开了十来颗。""拨开了几十颗。""拨开了一大群,漂亮。"
- 每局结算 = 时间段整句 + 躲避段整句 + "单击再飞一次,下滑回大厅。"
- **里程碑播报**:`dodges % 10` 的数字拼接改为 5 句固定整句(10/20/30/40/50 颗各一句:"十颗了,稳。"…"五十颗,传奇。"),超出 50 不再播报。
### 4.3 最高分
存活时间最高纪录存进度(`data = { bestSec: n, totalRuns: n }`),结算时若破纪录加一句"新纪录!"(SUCCESS 震动)。
## 5. 震动映射(对齐 00 文档)
| 事件 | 震动 |
|---|---|
| 成功闪避 | SWIPE_OK(dodge 音保留) |
| 警报渐强(撞击前 1s / 0.5s 两段) | DANGER 渐强脉冲(随 alarm 两档,第二档更密) |
| 撞击 | HIT |
| 护盾挡下 | CONFIRM |
| 加速/护盾激活 | CONFIRM |
| 坠毁 | ERROR + HIT(间隔 300ms) |
| 演练做对 | SWIPE_OK;演练做错 ERROR |
现版 `sfx/alarm` 两档警报音**保留**(它是方位+距离的内容信息,不是反馈提示音),震动 DANGER 与其同步叠加。
## 6. 实现要点(给开发 agent)
1. `ESCAPE_SWIPE` 改同向映射(一行改动),`DODGE_WINDOW_MS`/`DODGE_GRACE_MS` 判定结构不动。
2. 新增 `phase='drill'`:复用 `_spawnObstacle` 但参数化(approachMs/spawnInterval/harmless 标志),演练陨石带 `harmless: true` 与指定 `dir`。
3. `_resolve()` 增加保护期分支:`now < protectionUntil` 时撞击走"训练护盾"路径(不掉血、播归因语音)。
4. 暂停刷怪:`_clearTimers()` 后重排 `_scheduleSpawn`(现版已有清晰的 timer 管理,game.js:250-261)。
5. 归因语音按方位选句:`voice/hit_left/hit_right/hit_up/hit_down` + `voice/hit_last`。
6. 语音全部整句重录:演练 8 句、保护期 3 句、单击/双击引入 4 句、归因 5 句、结算 10 句(时间段 4 + 躲避段 4 + 新纪录 + 重开)、里程碑 5 句 ≈ 35 句 × 2 音色。删除全部 num 拼接件。
7. 教学语音与 whoosh 的时序:语音 onComplete 后再 spawn 演练陨石,保证不叠放。
8. 更新 `web/space/README.md` 手势表(同向闪避是玩家可见的最大变化,README 必须同步)。
---
## 修订记录 R5(space polish:刺耳 + 状态/动作反馈盲区,research/space-polish/)
取证(round1):live 两会话(research/space-polish/round1/live-{A,B}-transcript.md)+
4 人设评审(同目录 p1-blind/p2-student/p3-office/p4-worker)+ 全 sfx 客观频谱
(同目录 sfx-spectrum-analysis.txt)。核心事实:
1. **刺耳(客观)**:alarm 每颗陨石响两遍且 ≥3kHz 能量 82.3%/≥5kHz 63.9%/峰值 16kHz;
dodge 真峰值 -2.30dBFS + 63.6%≥3kHz;shield_block -2.60+52.2%;boost -2.70+53.0%;
shield -2.90 超标。四件最高暴露音效全部违反 one-shot 标准(≤-3dBFS)。
2. **保护期结束死亡窗(P0,live A t+151-163 与 t+300-310 两轮复现)**:裸飞期逼近 4.0s
= 刷怪 4.0s,每颗撞击与下一颗 spawn 同窗——呼喊维持生存优先(顶断归因)的代价是
hit_left/hit_down **零毫秒被杀**,玩家 8 秒三连撞至死不知方位;protect_end 亦被顶得
只剩 0.5-3.5s。
3. **保护期 protect_* 七连截断**:6.96s 教学句对 4.0s 刷怪间隔,每句播 4.0s 就被
下一句顶断(live A 7 连 TRUNCATED),信息重复又残缺。
4. **动作反馈盲区**:加速冷却中单击全静默(护盾拒绝有回音、加速没有——归因真话不均);
挥空/滑错方向与成功闪避同静默(dodge 音只在判定时才响);boost/shield 激活无
CONFIRM 震(§5 承诺未兑现)。
R5 裁定与实现(逐条对应 playtest.mjs 新断言):
- **R5-1 刺耳重合成**:alarm/dodge/shield_block/boost/shield 五件程序合成重制
(`tools/gen_space_syn.py`,低谐波设计:alarm 双音 700/560Hz、dodge 中频气声扫频,
真峰值 ≤-3.4dBFS、编码后 ≥3kHz 能量 0%,解码回读校验)。SA3 非必需,程序合成达标。
新增 `sfx/whiff`(挥空轻叹,0.55 音量)。
- **R5-2 归因真话·防顶断 replay**:呼喊生存优先裁定**不变**(呼喊照常同窗直出、照常
顶断归因);但 hit_<dir>/protect_end 起播经 `_defendVoice` 登记,被顶断时登记残留,
呼喊播完的 onComplete 走 `_replayDefended` queue 接 replay(不顶新威胁信息),
保证每句归因/保护结束宣告完整送达至少一次;归因 replay 播完仍接 hit_last(先因后果)。
- **R5-3 protect_* 去重**:上一句 protect_<dir> 在播时新吸收只发 shield_block 音 +
WARNING 震、跳过语音,每句教学完整播完(不再 7 连截断同一句开头)。
- **R5-4 加速冷却拒绝**:`_deny(now, voiceId)` 共用 4s 去抖(护盾拒绝同路径、原句
feat_shield_done 不变);加速冷却单击 → HEARTBEAT 震 + 新句 `boost_not_ready`
「引擎还在冷却,稍等一下。」。
- **R5-5 protect_soon**:开局第 50s 播报「训练护盾还剩十秒。」(queue 不顶句;
菜单/分批引入打断由 _rearmRunTimers 补播),给反应慢的玩家 10s 提前量。
- **R5-6 激活确认**:boost/shield 激活(对局期与 feature 期)补 CONFIRM 震(兑现 §5)。
- **R5-7 挥空反馈**:playing 期滑向无悬置陨石的方向 → `sfx/whiff` 轻叹(引擎 swipe
震动已有"收到",whiff 补"无用"),与 dodge 成功音形成对偶。
- **状态查询手势**:维持 00 §5 豁免表裁定(四向滑=闪避,space 无对局期 status key;
坠毁结算后上滑=重听教学要点)。状态透明全部由事件反馈句承担(本 R5 已补齐
保护期倒计时/冷却拒绝/归因可达性),不新增手势。