Files
SoundGame/docs/games/escape.md
T
SoundGame Dev f0f0a9b50e 初始提交:3 款纯音频游戏 + 共享引擎 + 完整文档与资产管线
- 共享引擎(app/src/main):GestureEngine 6 手势识别、AudioEngine 三层音频
  (ExoPlayer BGM 交叉淡入 / 对白 ducking / SoundPool 低延迟)、Game/GameEngine
  状态机接口、MainActivity 全屏封面 + TalkBack
- 《声命线》storyline:末世互动广播剧,3 幕 8 抉择点 4 结局
- 《声东击西》reflex:听声辨位反应,12 波战斗 + BGM 三档 BPM 变速
- 《暗室》escape:3x3 网格密室逃脱,钥匙→铁柜→保险丝→电箱→大门谜题链
- 文档:setup(从零搭建)/ architecture(架构)/ engine-api(引擎 API)/
  asset-pipeline(资产流水线)/ games/*(三款游戏设计文档)
- 资产管线:Qwen3-TTS 对白 + 程序合成音效/BGM + Mureka BGM 的 gen 脚本与台账,
  全部 ogg 资产入库可复现
- env.d 环境脚本:android/tts 入库;sfx/mureka 含密钥不入库,仅提交 .example.sh 模板
2026-08-29 13:00:34 +08:00

103 lines
5.0 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.
# 《暗室》(escape) — 密室逃脱解谜 设计文档
- 包名:`com.soundgame.escape`
- 代码:`app/src/escape/java/com/soundgame/game/EscapeGame.kt`
- 生成脚本:`app/src/escape/gen/`(`gen_voice.py` / `gen_sfx.py` / `gen_cover.py`)
在 3x3 全黑房间网格中靠环境音辨位探索,按"钥匙 → 铁柜 → 保险丝 → 电箱 →
大门"的解谜链逃出地下设施。逃脱后播报用时,可单击重开竞速。
## 手势映射
| 手势 | 含义 |
|---|---|
| 上/下/左/右滑 | 在相邻房间间移动;撞墙播闷响(首次撞墙追加一条语音说明) |
| 单击 | 检查当前房间 / 拾取物品 |
| 双击 | 使用物品(钥匙开铁柜 / 保险丝修电箱 / 通电后开大门);无可用目标时兜底播报背包清单 + 当前目标提示 |
**设计说明**(代码注释原文):四向滑动全部用于移动——3x3 网格必须占用四个
方向;原需求中"上滑=背包 / 下滑=方位提示"与四向移动冲突,故背包与提示合并到
"双击且无可用目标"的兜底反馈中。
## 房间地图与谜题链
```
x: 0(西) 1 2(东)
y=0(北) 电箱房·嗡鸣 风道·风声 空房·滴水
y=1 空房·风声 起点·滴水 铁柜房·嗡鸣
y=2(南) 储物间·滴水 空房·嗡鸣 大门·风声
```
环境音线索已烘焙立体声方位感:滴水偏左、风声双声道 LFO 错相、嗡鸣右偏带
电流嘶声(见 gen_sfx.py)。进房间时播放一次性线索音(`playAmbient`)。
谜题链(严格线性):
```
储物间(0,2) 单击拾取【钥匙】
│
v
铁柜房(2,1) 双击用钥匙开柜 → 得【保险丝】(钥匙消耗) sfx: unlock + door_creak
│
v
电箱房(0,0) 双击装保险丝 → 恢复供电(保险丝消耗) sfx: spark + power_on
│
v
大门(2,2) 双击开门 → 逃脱 sfx: gate_open + win_chime
BGM 2.5s 淡出,播报用时
```
每步的"检查"语音会随进度变化(如大门:未通电 `inspect_gate` → 已通电
`inspect_gate_ready`),兜底提示链 `hint_key → hint_cabinet → hint_box →
hint_gate` 始终指向下一个目标。
## 流程阶段
`Phase.INTRO`(旁白 → 教学,单击可逐段跳过,`introStep` 去重防止 onComplete
与跳过同时推进)→ `Phase.PLAY` → `Phase.ENDING`(单击重开,跳过教学直接
回 PLAY,`voice/replay` 过渡)。
用时播报用 0-9 + "十/分/秒" 数字语音拼接(`numberVoices`,支持 0..99),
格式:`time_prefix + [N分] + N秒 + again`。
## 角色音色分配(gen_voice.py)
全部 41 条语音统一使用 **Uncle_Fu**(低沉醇厚中年男声,契合黑暗密室氛围),
仅靠 instruct 区分三种语气:
| instruct | 用途 |
|---|---|
| NARRATE 低沉平稳、神秘紧张 | 旁白、房间描述、检查反馈 |
| HINT 清晰耐心、语气平和 | 教学、提示、背包播报、数字/单位 |
| RELIEF 如释重负、明亮激动 | 逃脱成功、用时播报前缀 |
## 资产清单
| 层 | 数量 | 内容 |
|---|---|---|
| voice | 41 | 开场/教学 3 + 检查与使用反馈 11 + 背包 4 + 提示 4 + 空房 3 + 用时播报 15(数字 0-9 + 十/分/秒 + 前缀/再来)+ replay 1(含 1 条未使用的 use_fail,见缺陷) |
| sfx | 13 | 交互 10(step/wall/pickup/ui_deny/unlock/door_creak/spark/power_on/gate_open/win_chime)+ 环境音 3(amb_drip/amb_wind/amb_hum,立体声) |
| bgm | 1 | drone.ogg(Mureka 生成,117s 无缝循环;原程序合成版备份在 tools/bgm_backup/) |
## 已知缺陷 / 遗留线索
- **`voice/use_fail.ogg` 是死资产**:gen_voice.py 生成了"这里没有用得上东西的
地方",但 `useItem()` 的兜底分支实际播的是 `ui_deny` 音效 + 背包 + 提示,
从未引用 use_fail。重新生成时可考虑删除该条,或把兜底改为先播 use_fail。
- 环境线索音只在进房间时播一次,停着不动就没有了;玩家原地迷路时只能靠
双击兜底提示,没有"重听环境音"入口(单击被"检查"占用)。
- 撞墙语音只播一次(`wallVoicePlayed`),此后只有闷响——有意减少重复,
但也意味着错过第一次就再听不到文字说明。
- 网格只有 3x3 且谜题严格线性,重玩价值主要来自竞速(用时播报)。
## 调参入口
| 位置 | 参数 | 控制什么 |
|---|---|---|
| `EscapeGame` 坐标判断 | 房间功能坐标(硬编码在 `playAmbient/inspect/useItem`) | 地图布局与谜题位置 |
| `useItem()` / `hintVoice()` | 解谜链条件与提示链 | 谜题结构 |
| `escape()` | `stopBgm(2500)` | 结局 BGM 淡出时长 |
| `gen_sfx.py` 各函数 | 合成参数(采样率 44.1kHz,环境音峰值 0.55-0.7) | 环境音方位感、音效质感 |
| `gen_sfx.py` `bgm_drone()` | 程序合成 BGM(50s 首尾 4s 交叉淡化) | 已被 Mureka 版替换,仅留作备份复现 |
| `gen_voice.py` `CHUNK = 8`、峰值归一 0.89 | TTS 批量与响度 | 生成效率、语音响度统一 |