《声东击西》(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 |
响度与体积 |