Files
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

5.9 KiB
Raw Permalink Blame History

《声东击西》(reflex) — 听声辨位反应 设计文档

  • 包名:com.soundgame.reflex
  • 代码:app/src/reflex/java/com/soundgame/game/GameFactory.kt (ReflexGame 与 GameFactory 同文件,与其他两款游戏的拆文件风格不同)
  • 生成脚本:app/src/reflex/gen/(gen_voice.py / gen_sfx_bgm.py / convert_audio.py / fix_num6.py)

敌人从四个方向来袭,玩家纯靠听声音方位做出对应手势。教学演练 → 12 波战斗 (节奏递增、BGM 三档变速)→ 结算播报得分。共 5 点血,血尽或通关进结算。

方位声音设计(gen_sfx_bgm.py)

方位 提示音 声学设计 应对手势
左 cue_left 0.45s 噪声扫频 whoosh,声像硬打左声道 左滑
右 cue_right 同上,硬打右声道 右滑
上 cue_up 高音上行琶音 E6-G6-B6(1319/1568/1976 Hz) 上滑
下 cue_down 低音下行轰击 110→55Hz + 闷响 下滑
正面 warn_front 急促三响警报(880/660 交替,约 1.0s) 单击格挡

正面警报特意做到 ~1s 且反应窗口强制 ≥ 1100ms(FRONT_MIN_WINDOW_MS), 用来覆盖单击因双击判定窗口产生的 ~300ms 回调延迟(见 docs/architecture.md)。

手势映射

手势 INTRO PRACTICE BATTLE ENDED
四向滑动 忽略 对应方向则过,错则重播提示 击杀对应方向敌人;划错断连击不扣血 忽略
单击 忽略 正面演练格挡 格挡正面攻击;无正面攻击不惩罚 重开(跳过教学)
双击 忽略 忽略 音爆冲击清屏(12s 冷却) 忽略

教学旁白(INTRO)期间完全不响应手势,防连点。

波次表(waveCfg,共 12 波)

波次 w 出怪数 4+w 出怪间隔 ms 反应窗口 ms 正面概率 BGM 档
1 5 2000 1700 7.3% calm (100 BPM)
2 6 1873 1614 8.6% calm
3 7 1746 1528 9.9% calm
4 8 1619 1442 11.2% tense (128 BPM) + 语音播报
5 9 1492 1356 12.5% tense
6 10 1365 1270 13.8% tense
7 11 1238 1184 15.1% tense + 语音播报
8 12 1111 1098 16.4% tense
9 13 984 1012 17.7% frantic (152 BPM)
10 14 857 926 19.0% frantic + 语音播报
11 15 730 840 20.3% frantic
12 16 600(触底) 750(触底) 21.6% frantic + 总攻语音
  • 出怪间隔公式:(2000 - (w-1)*127).coerceAtLeast(600)
  • 反应窗口公式:(1700 - (w-1)*86).coerceAtLeast(750);正面攻击取 max(窗口, 1100)
  • 正面概率公式:0.06 + w*0.013
  • 每波首怪在波开始后 1400ms 出现;波间间隔 1800ms

计分与状态

  • 击杀:100 + 10 × 当前连击;技能击杀固定 50/个;连击 5/10/15(每5) 有音效, 连击 10 追加战友语音夸奖。
  • 漏怪:扣 1 血、断连击;HP=2 / HP=1 时各播报一次警告语音。
  • 双击技能:清空全部在场敌人,冷却 12s,冷却完成有语音提醒,冷却中双击 播 skill_denied。
  • 结算:逐位播报得分(num_0..num_9 数字语音拼接),破纪录播 new_record, 否则播报历史最高。最高分为进程内变量,杀进程即丢。
  • 时间调度全部走主线程 Handler(手势回调也在主线程),无需加锁; onStop 清空全部回调。

角色音色分配(gen_voice.py)

角色 speaker instruct 负责内容
教官/旁白 Uncle_Fu(低沉醇厚中年男声) 战场教官语气(教学一字一句、战前激昂、重伤焦急等逐条指定) 教学 tut_01-07、战报 wave_04/07/10/12、血量警告、胜负播报、报分、数字 0-9
战友 Vivian(明亮略带锋芒年轻女声) 兴奋赞叹/轻快提醒/惊喜欢呼 combo_praise、skill_ready、new_record、restart_hint、replay

资产清单

层 数量 内容
voice 33 教学 7 + 战报 4 + 血量 2 + 激励 3 + 胜负/报分 6 + 重开 1 + 数字 10
sfx 18 方位 cue 5(cue_left/right/up/down + warn_front)+ 反馈 5(hit/block/hurt/skill/skill_denied)+ 连击 3 + wave_horn + ui_start + warmup + victory_fanfare + gameover_sting
bgm 3 bgm_calm / bgm_tense / bgm_frantic(程序合成脉冲节奏,100/128/152 BPM 各 8 小节,无缝循环)

BGM 保留程序合成是有意为之:玩法要求三档 BPM 随波次精确切换,AI 生成曲目 BPM 不精确会破坏节奏机制(见 README 待办与 mureka_log.md)。

已知缺陷 / 遗留线索

  • fix_num6.py:数字"六"的 TTS 生成不稳(单次生成时长经常超界), 单独写了个重试脚本(最多 4 次,取 0.3s~1.5s 的一条)。重新批量生成 gen_voice.py 时 num_6 可能再次翻车,需重跑 fix 脚本。
  • warmup.wav 是假资产:0.1s 近静音文件(+1e-4 绕过峰值校验), 只为 SoundPool 预热存在。
  • 最高分不持久化:highScore 是 companion object 的 var,进程内有效。
  • 划错方向只断连击不扣血、无正面攻击时单击不惩罚——为掩盖单击延迟的 有意宽容设计,严格模式下可视为难度漏洞。
  • 结算逐位报分在长分数时较拖沓(每位一条独立语音文件)。

调参入口

位置 参数 控制什么
ReflexGame companion MAX_HP=5 / TOTAL_WAVES=12 / SKILL_COOLDOWN_MS=12000 / FRONT_MIN_WINDOW_MS=1100 血量/波数/技能冷却/正面窗口下限
waveCfg() attacks/interval/reaction/frontProb 四个公式 难度曲线核心
startWave() BGM 切换阈值(w≥4 / w≥9)、语音播报波次 节奏与叙事节拍
kill() 100/50 基础分、连击加成 10×combo 计分
gen_sfx_bgm.py cue 音频率/声像、make_pulse_bgm(bpm, bars) 方位可辨度、BGM 速度与密度
convert_audio.py voice 峰值归一 0.6、-q:a 4/4/3 响度与体积