- 共享引擎(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 模板
103 lines
5.0 KiB
Markdown
103 lines
5.0 KiB
Markdown
# 《暗室》(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 批量与响度 | 生成效率、语音响度统一 |
|