Files
SoundGame/docs/design/24-rhythm-drummer.md
T
xsl ae850a4ce0 feat(rhythm): 鼓手模式重构——屏幕六区鼓组 + 无鼓伴奏合拍(用户裁定重构)
- 屏幕3列×2行六区鼓组(列=声像左中右/行=音高上亮下沉),六件程序合成打击乐,三重冗余蒙眼可辨
- 无鼓伴奏BGM上跟随领奏 call&response + 加花句;3曲90/105/120 BPM解锁制,BPM实测89.99/105.00/120.01
- 引擎后向兼容扩展:onGesture补{x,y}坐标(单参回调零感知,15游戏+3引擎测试全量回归零失败)
- 判定简化:全部音符=分区点按零延迟,旧双击补偿/下滑折中退役;键盘Digit1-6+空格=家区
- 教学摸鼓认区(摸哪报哪,8s无触摸自动进阶)+重教≤2+降速DDA+归因真话律
- 保留旧作好评:跟随领奏核心/S~C评级/解锁制/判定口径/guide工程遗产
- 语音49id双性别+8key;R1+R2连续2轮4/4愿意玩P0=0裁定可交付;live直证33 Perfect dt≈0.00s
- 03号文档仅文末加取代指针;新设计24号;档案research/rhythm-drummer/;Mureka累计$1.35
2026-09-14 20:17:10 +08:00

25 KiB
Raw Blame History

24 · 《节拍行者》鼓手模式重构设计(rhythm / Drummer Mode)

用户裁定:现有模式「不错,保留」——跟随领奏的合拍玩法核心、S~C 评级、曲目解锁制、 BPM 精确性延续;整体重构为屏幕分区打击乐:屏幕划分区域,每区一种打击乐音效, 玩家作为乐队鼓手与无鼓伴奏音乐合拍。本文取代 03-rhythm-rebuild.md 的全部施工依据(03 号文档仅作历史档案,文末有取代指针;其 rhythm 判定好评口径 「PERFECT ±0.15 / GOOD ±0.35」经 live 校准,本文沿用)。

语音台词全表在 24-rhythm-voice-ids.md。断言清单在 §12。


0. 可行性勘察决策(阶段零结论)

0.1 引擎手势事件是否携带坐标?

不携带。 web/engine/gesture.js 的 onGesture(g) 只传手势名字符串, 触摸坐标只在引擎内部用于滑动/点按判定,回调时丢弃。

0.2 两条路线的权衡

(a) 引擎扩展:第二参数透传坐标 (b) 游戏层自建 touch 监听
改动面 gesture.js 每个 onGesture 调用点补 {x,y} 第二参;docs/web-engine-api.md 同步 游戏层加 touchstart 记录坐标
既有游戏影响 零——全部 15 款游戏的回调都是单参 (g) =>,多余实参被忽略,手势分类/震动/时序逐字节不变(同 opts.pan / immediateTap / tapcancel 先例) 零(不碰引擎)
键盘通道坐标 免费:空格虚拟指针本就走 handleDown(keyCenter),天然拿到元素中心坐标 无法区分键盘 tap 与残留触摸坐标,需自行加时效启发式(脏)
保持的引擎福利 haptics 自动震动、多指守卫、tapcancel、长按快进 window 事件、双击状态机、键盘三通道全部保留 全部丢失,需在游戏层重写(双击窗/tapcancel/快进事件)
代价 全量回归:15 游戏 playtest + guide.test + engine-voice + engine-input + docs 同步(铁律) 回归只需 rhythm 自身,但收获一个与引擎并行的第二输入状态机(长期维护债)

0.3 决策:路线 (a),引擎后向兼容扩展

  • attachGestures 的全部 onGesture(...) 调用补第二实参 {x, y}(视口像素 clientX/clientY 语义):tap 类事件(含 immediateTap 序曲 tap、双击改判、tapcancel) 与 longpress/longpressend 传按下点;四向滑动传滑动起点(出手瞄准位置); 键盘通道传虚拟指针坐标(keyCenter(),方向键滑动同样补)。
  • 缺省行为逐字节不变:不加 config 开关、不改任何判定阈值与震动;回调签名扩为 (g, info?: {x, y}),单参回调零感知。
  • 回归义务(已执行,见 research/rhythm-drummer/round0/engine-regression.md): 15 游戏 playtest + web/harness/guide.test.mjs + engine-voice.test.mjs + engine-input.test.mjs 全绿,docs/web-engine-api.md §gesture 同步。

0.4 打击乐音色:程序合成

六区音色 + accent 全部 numpy 程序合成(tools/gen_rhythm_syn.py, gen_cipher_syn.py 同款工艺):可精确控制起音/衰减(盲玩靠起音对拍)、 零授权问题、零网络延迟、与 BGM 调性关系无关(打击乐无调性)。

0.5 BGM:无鼓伴奏是硬需求

玩家就是鼓手——BGM 含鼓则叠打混乱。Mureka instrumental 生成时 prompt 显式 no drums, no percussion,tools/bpm_precise.py 梳状扫描实测 BPM (节奏类硬性要求,误差 >0.5 则 atempo 拉伸,R3 track90 先例), 无缝循环后处理沿用批次 1-4 工艺(强 onset 起点 → 整数小节裁剪 → 首尾 3s acrossfade → loudnorm I=-20 → ogg q3 + m4a 128k)。


1. 概述与继承/舍弃清单

一句话玩法:屏幕是一面 3列×2行 的鼓组。系统(领奏)在无鼓伴奏上敲一小节 节奏型,玩家在下一小节的相同拍点敲相同的鼓。曲子从 90 BPM 打到 120 BPM, 拿 B 级以上解锁下一首。

1.1 继承自旧作(用户裁定「保留」的部分)

项 说明
跟随领奏 call & response 核心循环 领奏 1 小节 → 应答 1 小节,逐乐句推进
时值判定口径 PERFECT ±0.15s / GOOD ±0.35s(live round1 §13 校准值,吸收蓝牙延迟)
计分与评级 Perfect=2/Good=1/Miss=0,rate = 得分/(2×总音符),S≥0.95 / A≥0.85 / B≥0.70 / C 其余;不掉血不中断,歌照唱完
曲目解锁制 曲目 n 拿到 B 级 → 解锁 n+1;localStorage + 已登录 PUT 同步
BPM 精确性 谱面/判定全部锚定 BGM 实测 BPM 网格(bpm_precise 实测 + atempo 修正)
工程质量遗产 AudioContext 预排程泵(40ms + LOOKAHEAD 0.6s)、BGM 手动 src.start(t0) 对网格、模态菜单断点续播、_speakGuarded 看门狗语音链、结算后 menu pad 看门狗、旁白单击跳过/长按快进豁免、guide 层全量接入(状态签名/gd_answer/gd_miss 归因/教学 DDA 降速/count-in 恢复)

1.2 舍弃/替换

旧 新 理由
手势→乐器映射(tap=kick、left=闭镲…6 手势 6 音色) 屏幕分区→乐器(6 区 6 音色,tap 即敲击) 用户裁定的新核心
滑动/双击作为音符类型(SWIPE_DELAY 0.15 / DOUBLE_DELAY 0.20 补偿、双击序曲撤销) 全部音符 = 分区点按,immediateTap 按下即判,零识别补偿 打击乐每次出手都是「敲一下」,滑动从音符类型降级为纯导航;双击=同区连击两下(两声都合法),不再需要双击序曲撤销账簿
BGM 含鼓组(track90/105/120 有鼓) 无鼓伴奏 玩家是鼓手
下滑呼菜单的「down 音符临近」折中(live P0 加窗/复活窗一整套) 下滑无条件呼菜单 音符全是点按,下滑永不与音符冲突;序曲 tap 仍出 ghost 音但判定时由 tapcancel 撤销
教学 T1-T3(手势记忆) 分区心智模型教学(摸鼓认区 → 跟打) 「摸到哪是哪」的建立是设计核心(§6)
旧语音表 31 句 新表 41 句(+沿用向导 8 句 = 49 句双性别,配额见 §8) 分区教学与区名播报需要新句

2. 屏幕分区与音色映射

2.1 分区布局(3 列 × 2 行,纯听觉可辨是硬约束)

┌─────────┬─────────┬─────────┐
│ TL 左上 │ TC 上中 │ TR 右上 │   上排:清亮/高频
│  踩镲   │  高嗵   │  吊镲   │   pan: -0.6 / 0 / +0.6
├─────────┼─────────┼─────────┤
│ BL 左下 │ BC 下中 │ BR 右下 │   下排:低沉/低频
│  军鼓   │  底鼓   │  低嗵   │   pan: -0.6 / 0 / +0.6
└─────────┴─────────┴─────────┘

双轴心智模型(教学的主话术):

  • 列 = 声像:左列在左耳、中列在中央、右列在右耳(opts.pan 引擎已支持)。
  • 行 = 音高:上排清亮、下排低沉。
  • 在双轴之上,六件乐器音色本身两两不同(镲/嗵/鼓),盲玩家有 「声像 + 音高 + 音色」三重冗余线索。

2.2 分区 ↔ 音色 ↔ 声像总表

区码 位置 乐器 sfx id pan 合成要点(起音硬、≤0.5s 残响)
TL 左上 闭合踩镲 sfx/dm_hat -0.6 6-10kHz 高通噪声,0.06s 急衰减
TC 上中 高音嗵 sfx/dm_tomhi 0 200→140Hz 正弦扫频 + 轻皮噪,0.25s
TR 右上 吊镲 sfx/dm_crash +0.6 4-12kHz 噪声 + 非谐波泛音列,1.2s 长尾
BL 左下 军鼓 sfx/dm_snare -0.6 200Hz 鼓皮 + 1-4kHz 带通噪声,0.2s
BC 下中 底鼓 sfx/inst_kick(沿用旧 id) 0 130→45Hz 正弦扫频 + 触击瞬态,0.25s
BR 右下 低音嗵 sfx/dm_tomlo +0.6 120→75Hz 正弦扫频 + 轻皮噪,0.3s
  • 附加层:sfx/dm_accent(Perfect 叠加亮音,程序合成木鱼 1.5kHz 短音)。
  • 分区判定:zoneOf(x, y) 以 #game-root 的 getBoundingClientRect() 为界, y 上半 = 上排、y 下半 = 下排;x 三等分 = 左/中/右。无布局环境(mock/harness) 由调用方显式传 payload;payload 缺失时落 BC(中央家区,兜底不炸)。
  • 键盘平权(游戏层 keydown,引擎键盘通道之外的补充,数字键引擎本就不拦截): Digit1/2/3/4/5/6(及小键盘)= TL/TC/TR/BL/BC/BR,按下即发、零延迟; 引擎空格 tap 落在元素几何中心 → 判 BC(「家」)。选曲/菜单/结算态数字键不生效 (那些态沿用手势导航)。

2.3 领奏音色 = 玩家音色

领奏音符与玩家敲击走同一批 sfx(同音色同声像)——做对时听到自己在和 「另一个鼓手」合奏,这是旧作好评的「演奏感」来源,保留。


3. 玩法循环与状态机

3.1 循环

选曲 → 数拍导入(1 小节底鼓×4,含节拍轻震) → 逐乐句(领奏 1 小节 + 应答 1 小节 [+ 休止])
     → 过门/加花(领奏 1 小节密集加花 + 应答 1 小节) → 终句吊镲 → 结算评级

3.2 状态机

idle → intro(语音) → tut(分区教学) → select(选曲,二周目起) → countin → main(逐乐句/加花) → end(结算)
                                                          ↑_________________|
  • 数拍小节由游戏排程 4 记数拍(BGM 无鼓,数拍只能靠鼓手自己)+ 每拍 [8] 轻震:正片用底鼓 inst_kick×4(guide.test G3 的 _sched 锚点,13 号冻结文档不可改),教学步用木鱼 click(dm_accent,guide.test G2 要求教学 _sched 里 kick=领奏音)。
  • 每曲第一个应答窗口前 1 小节落 gd_answer + turn earcon(旧作 G1 修正沿用)。
  • 模态菜单(下滑呼出):单击循环 [继续演奏 / 回大厅]、双击确认、上滑帮助、 下滑关闭;暂停真冻结(泵停、BGM 断点记 offset),恢复时全网格平移 + 4 拍 count-in(guide full 档先播 gd_count,每局限 1 次,round3 G3 全套沿用)。

4. 谱面与难度曲线

4.1 记号

拍:区码,1 小节 4 拍,x.5 = 八分反拍。乐句 = 领奏 1 小节 + 应答 1 小节; 加花 = 特殊乐句(fill 标记),领奏为八分音符密集型,最后一音恒为 TR 吊镲收束。

4.2 曲目一《启程》90 BPM(教学曲,8 句 + 终曲加花,≈65s)

句 领奏节奏型 音符数
1 1:BC, 3:BC 2
2 1:BL, 3:BR 2
3 1:BC, 2:BC, 3:BL 3(双击底鼓引入同区连击)
4 1:BL, 2:BR, 3:BC 3
5 1:BC, 2:BL, 3:BC, 4:BR 4
6 1:TL, 3:BL, 4:BC 3(上排首次进正片)
7 1:BC, 2:TL, 3:BC, 4:TR 4
8 1:BL, 2:BR, 3:TL, 4:BC 4
终曲加花 1:BC, 1.5:BC, 2:BC, 2.5:BL, 3:BL, 3.5:BC, 4:TR 7(全曲唯一反拍,仅限加花)

前 4 句应答后各休 1 小节,其余不休。

4.3 曲目二《追风》105 BPM(10 句 + 中途加花 + 终曲加花,≈80s)

句 领奏节奏型 音符数
1 1:BC, 3:BR 2
2 1:BL, 2:BL, 3:BC 3
3 1:TL, 3:BL, 4:BR 3
4 1:BC, 2:BL, 3:BC, 4:TL 4
5 1:BC, 2.5:BR, 3:BC 3(首个反拍)
中途加花 1:BL, 1.5:BL, 2:BC, 2.5:BC, 3:BR, 3.5:BR, 4:TR 7
6 1:BL, 2:TL, 3.5:BR, 4:BC 4
7 1:BC, 2:BL, 3:BR, 3.5:TL 4
8 1:BR, 1.5:BL, 3:BC, 4:TR 4
9 1:BC, 2.5:TL, 3:BC, 4:BL 4
10 1:BL, 2:BR, 3:TL, 4:BC 4
终曲加花 1:BC, 1.5:BL, 2:BC, 2.5:BL, 3:BC, 3.5:BR, 4:TR 7

前 2 句应答后各休 1 小节;第 5 句与中途加花之间不休。

4.4 曲目三《星爆》120 BPM(10 句 + 中途加花 + 终曲加花,≈75s)

句 领奏节奏型 音符数
1 1:BC, 2:BL, 3:BC, 4:BL 4
2 1:TL, 2:BL, 3:TL, 4:BC 4
3 1:BR, 2:TR, 3:BR, 4:BL 4
4 1:BC, 2.5:TL, 3:BC, 4:BR 4
5 1:BL, 1.5:BR, 2.5:BL, 3:BC 4
6 1:BC, 2:TL, 2.5:BL, 3:BC, 4:TR 5
中途加花 1:TL, 1.5:TL, 2:BL, 2.5:BL, 3:BC, 3.5:BC, 4:TR 7
7 1:BR, 2:BC, 3:BL, 3.5:BR, 4:TL 5
8 1:BC, 1.5:BC, 2.5:BL, 3:BC, 3.5:TL, 4:TR 6(高潮句)
9 1:BL, 2:BC, 2.5:BR, 3:TL, 4:BL 5
10 1:BC, 2:BR, 3:BC, 3.5:TL, 4:BL 5
终曲加花 1:BC, 1.5:BL, 2:BC, 2.5:BL, 3:BC, 3.5:BC, 4:TR 7

全曲不休。

4.5 难度曲线规则(改谱遵守)

  1. 乐句长度:曲目一 2→4 音、曲目二 2→4 音、曲目三 4→6 音;同曲单调不减 (终曲加花不计入)。
  2. 反拍(x.5):曲目一只允许出现在加花;曲目二第 5 句起、普通乐句每句至多 1 个反拍音;曲目三不限。加花为八分音符基底,不受此限。
  3. 同一分区连续出现 ≤2 次(同区双击是合法手法,三连击禁止——盲玩极限)。
  4. 同一乐句内分区种类 ≥2(纯底鼓句仅允许曲目一句 1)。
  5. 相邻反拍连续位禁止左列↔右列大跳(BL↔BR 相邻反拍只允许曲目三)—— 声像跳变过快在耳机上易混(旧作 §3.5-6 同款)。
  6. 加花尾音恒为 TR 吊镲(收束感 + 吊镲长尾盖住应答结束)。

5. 时值判定与计分评级

const PERFECT = 0.15;   // Perfect 窗口 ±150ms(live round1 §13 校准)
const GOOD = 0.35;      // Good 窗口 ±350ms(吸收蓝牙延迟)
const LOOKAHEAD = 0.6;  // 调度前瞻(秒)
const PUMP_MS = 40;     // 调度泵周期
  • 判定时刻 = 触摸/按键时刻:immediateTap 按下即发 tap(零补偿); 键盘数字键 keydown 即发(零补偿)。旧作的 TAP_DELAY/SWIPE_DELAY/DOUBLE_DELAY 补偿与旧引擎闩锁、双击序曲撤销、超时复活窗整体退役(没有滑动/双击音符了)。
  • 归属:同分区最近未判定音符 |dt|≤GOOD → 出手即播该区音色(pan 随行), Perfect 叠 sfx/dm_accent;|dt|≤GOOD×0.5 的异分区音符被敲 = 判 Miss (错鼓);窗口外 = 自由演奏(出声音不判定)。
  • 序曲 tap 治理(immediateTap 代价,旧作账簿简化版):每次按下先发 tap (出声+可认领),转滑动/长按/触摸取消时引擎补 tapcancel → 撤销该 tap 的 认领(声音收不回当 ghost 音,判定/统计必须更正且不计 Miss)。双击不改判 账簿——两下敲击各自独立成立。
  • Miss 超时:expected + GOOD + 0.10s 未应答 → 闷镲反馈音 (sfx/dm_hat 降调 0.5× 复用,零新资产)+ error 震动。
  • 长按 = 死输入:Miss 反馈音 + error 震动,不判定不归属(演奏期长按快进 setRateBoostEnabled(false) 豁免照旧)。
  • 计分/评级/连击:Perfect=2 / Good=1 / Miss=0;rate = 得分/(2×总音符); S≥0.95 / A≥0.85 / B≥0.70 / C 其余;16 连击 success 震、乐句全 Perfect [20,40,20]、结算 S/A success 震,全部沿用旧作。

6. 教学编排(分区心智模型,设计核心)

蒙眼玩家如何建立「摸到哪是哪」?分四步,全部零惩罚(教学不计分、不出 Miss 音),单步重教 ≤2 次(第 2 次失败 → 领奏降速 25% 重演一次 → 再败跳过, 旧作 G2 DDA 沿用):

步 内容 机制
T0 摸鼓认区 tut_map 讲双轴模型(横三列=左中右声像,竖两行=上亮下沉)→ tut_probe 进入探索模式:随便摸,敲到哪区就播哪区音色 + 区名(zone_* 六句) 摸到 ≥3 个不同区自动进 T1(每区名每轮教学只播一次,重复摸只出声不报名,防刷屏);防呆:8s 无触摸自动进 T1(摸过 1-2 区再宽限 8s),探索是礼物不是门槛——guide.test G2 等无触摸驱动依赖此路径
T1 家区底鼓 tut_z_kick:BC 是家。数拍 1 小节 → 领奏 1:BC → 下一小节应答 做错:纠正链(§7 归因)→ 重播领奏重试
T2 左右下排 tut_z_sides:BL 军鼓、BR 低嗵。领奏 1:BL, 3:BR → 跟打 同上
T3 上排三音 tut_z_patt:TL 踩镲、TC 高嗵、TR 吊镲。领奏 1:TL, 2:TC, 3:TR → 跟打 同上;通过后 tut_done 进曲目一
  • 教学在曲目一无鼓 BGM 上进行;每步领奏前都有 1 小节底鼓数拍(教学期 count-in 不省)。
  • 下滑跳过教学(tut_skip)保留;教学 Miss 归因真话律沿用(没出手报 「这拍没接」gd_miss_none,不谎报早晚)。
  • 加花预告:tut_fill「最后有加花,跟着我连打。」在曲目一终曲加花领奏 前 1 小节播,每账号一次。

7. 连击/错拍反馈

时机 反馈
出手敲对 该区音色(pan 随行)+ Perfect 叠 accent
敲错区(异分区音符半窗内被敲) 该区音色照样出声(自由演奏语义)+ 闷镲 Miss 音 + error 震 + 播报实际敲中的区名(zone_*)+ retry_wrong「敲错鼓了,再听一遍。」(教学期)/ gd_miss 归因(正片,连 2 Miss 一次)
早/晚(窗内同区但超 Perfect) 不出错音(Good 照播),教学期事后归因 retry_early/retry_late
没出手 音符超时闷镲 + error 震;教学归因 gd_miss_none「这拍没接」
16 连击 / 乐句全 Perfect / 解锁 / S·A 结算 success 震 / [20,40,20] / success+[20,40,20] / success(沿用旧作)
曲目过半 mid_song(ducking 到 1/4 的旧作工艺沿用)

8. 语音配额(拥挤控制)

打击乐密度高,语音是稀缺通道。全表 49 id 双性别(41 新 + 8 向导沿用), 运行时配额:

  1. 应答小节内零语音(含 guide 插播——idle-only 语义天然保证)。
  2. 教学纠正:每次失败至多 1 条纠正链(归因 1 句 + 重听 1 句),同一步不叠加。
  3. 正片:连 2 Miss 才播 gd_miss,同曲一次;mid_song 每曲一次; 加花预告 tut_fill 每账号一次。
  4. 探索模式区名播报每区每轮教学一次(重复摸只出声)。
  5. 结算链 = grade + [new_best_combo] + [unlock] + restart,无其他插话; 结算 BGM 看门狗按链长缩放(旧作 P1 修复沿用)。
  6. 语音风暴保险丝(引擎 3s/5 次)是上届,游戏层不得绕过它连播同 id。

9. 音频资产规格

9.1 打击乐 sfx(程序合成,tools/gen_rhythm_syn.py 新建)

7 件 one-shot:inst_kick(沿用旧 id)/ dm_snare / dm_hat / dm_tomhi / dm_crash / dm_tomlo / dm_accent。QC 红线(gen_cipher_syn.py 同款):去直流 + 首尾 8ms cos² 淡化 + 编码后解码回读真峰值 ≤ -3dBFS(MDCT 过冲坑,超限自动降档重编)。起音必须硬 (盲玩对拍靠起音);kick/snare/hat ≤0.3s,tom ≤0.4s,crash ≤1.3s,accent ≤0.1s。

9.2 BGM(3 首无鼓伴奏,Mureka ≤$0.30)

曲 BPM prompt 特征
track90《启程》 90 bright optimistic pop rock backing, bass guitar + strummed acoustic + light synth pads, no drums no percussion, steady 90 bpm, A minor
track105《追风》 105 driving energetic pop backing, punchy bass + rhythmic guitar chops + synth, no drums no percussion, steady 105 bpm, A minor
track120《星爆》 120 high energy synth rock backing, aggressive bass + arpeggio synth + power chords, no drums no percussion, steady 120 bpm, A minor
  • bpm_precise.py 实测:|偏差|>0.5 BPM 则 atempo 拉伸到标称(R3 track90 先例), 成品复测记录台账。
  • 后处理:强 onset 起点 → 整数小节裁剪(时长为 4 拍整数倍)→ 末 2 拍与开头 acrossfade(tri) 无缝循环 → loudnorm I=-20/TP=-2/LRA=11 → 44.1kHz → ogg q3 + m4a 128k。
  • 旧 track90/105/120(含鼓)删除;bgm/menu(Mureka 状态曲,无鼓问题不影响 玩法——menu pad 不与演奏同响)保留沿用。

9.3 语音

见 24-rhythm-voice-ids.md:41 新 id 双性别(Qwen3-TTS,flock GPU), gen_voice_dual.py rhythm 段全量替换;--check(时长/峰值/两性离群)+ --convert(ogg q3 + m4a 128k)。废弃 id(tut_1/tut_2/tut_3/retry_gesture 及旧 welcome/rules/retry_* 文案)的音频文件随 convert 后清理删除。


10. 进度存储

沿用旧作 schema 1 结构({tutorialDone, unlocked, grades, bestCombo, summary}), key soundgame.rhythm.progress 不变;PUT /api/progress 带 summary (《曲名》X级 · 已解锁N/3首 取最高评级曲目)。解锁规则不变(B 级解锁下一首)。 结算手势:单击=再来一遍(同曲)、右滑=下一首(已解锁)、下滑=回大厅,不变。


11. 断言清单(web/rhythm/test/playtest.mjs 覆盖全集)

13 号文档 §4 rhythm 段是旧玩法的断言,已被本文取代但不回改(13 号 文档冻结);本节是 24 号文档的断言明细,按 §编号可被测试与评审引用。

  • A1 分区映射:zoneOf 3×2 九宫边界正确(角落/边界点逐一断言);payload 缺失兜底 BC。
  • A2 判定窗:|dt|≤0.15 → Perfect(+accent 叠加);0.15<|dt|≤0.35 → Good; |dt|>0.35 同区出手 = 自由演奏(出声不判定、不判 Miss)。
  • A3 错鼓 Miss:异分区音符 GOOD 半窗内被敲 → Miss(闷镲 + error 震 + combo 清零;区名播报限教学期——正片错音的归因就是那一声敲错的音色本身,不再叠语音(配额)。
  • A4 超时 Miss:expected+0.45s 无应答 → Miss(GOOD 0.35 + 放宽 0.10)。
  • A5 序曲 tap 撤销:immediateTap 按下认领 → 引擎 tapcancel → 认领撤销 且不计 Miss(统计回滚)。
  • A6 双击=两连击:300ms 内同区两 tap + doubletap → 两次敲击各自独立 (可认领两个相邻音符/一次认领一次自由演奏),无撤销、无崩溃。
  • A7 教学流程:T0 探索摸 3 区(区名每区一次)→ T1-T3 跟打通过 → tutorialDone;全程零 Miss 音、零计分。
  • A8 教学重教:单步失败 2 次 → 降速 25% 重演(gd_slow,每教学局 1 次)→ 再败跳过(retry_skip)不卡死;归因真话律(没出手 → gd_miss_none)。
  • A9 数拍:曲目开始先排 4 记底鼓 count-in(1 小节)再进句 1 领奏; 网格对齐 BGM t0。
  • A10 菜单:main 中下滑无条件呼菜单(无 down 音符折中);暂停冻结 (泵停、BGM 断点);恢复后 cue.expected/_bgmT0 平移量 = 暂停时长 + count-in 小节;guide full 档恢复先播 gd_count(每曲限 1 次)。
  • A11 加花:fill 句领奏为八分密集 + 尾音 TR;应答窗口与普句同规则; 曲目一终曲加花前 1 小节播 tut_fill(每账号一次)。
  • A12 结算评级:全 Perfect → S + grades.song1==='s' + unlocked===2 + unlock_song2 播报;B 级也解锁;C 级不解锁。rate 公式与阈值逐档断言。
  • A13 结算导航:单击=同曲重开、右滑=下一首(未解锁播 song_locked)、 song3 末位右滑播 restart 不播 song_locked(旧作裁定①回归)。
  • A14 连击里程碑:16 连击 success 震、乐句全 Perfect [20,40,20]。
  • A15 mid_song:曲中点播报且领奏 duck 到 1/4、播完恢复。
  • A16 进度同步:PUT body 含 summary/grades/unlocked/bestCombo;B 级提升 才覆写 grades。
  • A17 语音配额:应答小节内零 voice 调度;gd_miss 同曲一次;探索区名 每区一次(转写级断言放 transcribe 剧本核)。
  • A18 键盘平权:Digit1-6 注入 = 对应区命中(零延迟判定);空格 tap (元素中心坐标)→ BC;选曲态数字键无效。
  • A19 guide 接入:select=menu / main=battle / end=settle 状态签名同点; 首个应答窗口前 1 小节 gd_answer+turn。
  • A20 资产完整:check_all_assets.py rhythm 段全绿(含新 sfx/bgm/voice 双性别)。
  • A21 旁白跳过:intro/tut(引导语音)/select/end 语音播放中单击=跳句(12 号 文档 §1.2);main 演奏期单击是演奏不跳。
  • A22 全局:启动遮罩前无音频;无未捕获异常;settings.voice 切换下一句生效; bgm=0 时不加载 BGM。
  • A23(R1 F3) 教学没出手失败的纠正链 retry_listen 只播一次(不二连复读)。
  • A24(R1 F1) 曲尾 3s 内连 2 Miss 不插播 gd_miss(结算链不被顶断)。
  • A25(R1 F2) 探针三连摸的三个区名全部播完不被掐断,教学句 queue 其后 (进阶延迟 150ms 起步:引擎入队判定在 await 加载之前,同步连发会误判)。

12. 资产预算与台账

  • 语音:49 id × 2 性别 = 98 文件(Qwen3-TTS,flock .gpu.lock,零 API 费)。
  • 打击乐:7 件程序合成(零成本)。
  • BGM:3 任务 × $0.03,每首至多 1 次重提 → 上限 $0.18(预算线 $0.30, 累计 $1.26/$2.00 → 本轮后 ≤$1.44+$0.18=$1.56 上限)。台账追加 tools/mureka_log.md。
  • 开发/测试/转写:零成本(复用 harness)。