下载 App
【聚光灯21天】DAY17–19·从能玩到能上线:音效动效配音重做
修改于10/2013 浏览 包含 AI 合成内容

DAY 13–16 把 H5 版做出来了。这三天做的是让它像个成品。
| 天 | 做了什么 |
|---|---|
| DAY 17 | 配音整段合并口径重做(134 条覆盖李斯全剧本)+ 四首主题曲换新词 + 五剧本图标 |
| DAY 18 | 音效系统(第三条音频通道)+ 引擎财富见底锁 + 上线素材 |
| DAY 19 | 动效体系(四个优先级批次全落地)+ 印章抽取 + 罗盘巡游测试 |


一、DAY 17:配音换了口径
旧口径的问题
DAY 13 的配音是按事件切的:
chensheng_c0_0-1_s1.ogg ← 事件 0-1 的第 1 段
chensheng_c0_0-1_o1.ogg ← 事件 0-1 的第 1 个选项结算
一条事件被切成 4–5 段,每段单独一个文件。
问题在播放节奏上。 玩家读到哪播到哪,听起来是断续的 —— 一段念完停一下,再念下一段。而叙事文本本身是连贯的一段话,不该被切成念白。
新口径:整段合并
intro → chapter.intro 全部段合并成一条
<事件id> → event.segs 全部段合并成一条
choice_<ACT-id> → 抉择卡 segs 合并成一条
choice_<ACT-id>_o<N> → 抉择卡第 N 个选项的结算
ending_<E>[_<变体>] → 结局「太史公曰」
crisis_<C-id> → 危机事件
corr_<N> → 历史修正
134 条覆盖李斯全剧本。
同时砍掉了两类:主线事件的选项结算、随机事件 R-* —— 2026-10-17 用户拍板。
这是"少即是多"的取舍:选项结算往往只有一两句,单独念反而打断节奏;
随机事件数量多、单条短,配上反而聒噪。
目录结构也跟着改
voice/<剧本>/<章cN>/<切片> 本章的
voice/<剧本>/_shared/<切片> 跨章共享的(choice_/ending_/crisis_/corr_)
查找逻辑:先查本章目录,查不到回落 _shared。
一条不维护清单的设计(沿用)
切片名纯靠推导,import.meta.glob 建索引:
查不到就是没这条配音;这也是用 glob 而不是「预生成 index.json」的原因 ——
清单会漂移,glob 不会。
四条现实约束(决定了写法)
1. 缓冲窗口只有几百毫秒 → 必须提前把切片拉下来,不能播到哪下到哪
2. 串行播放,不能并发 → 并发拉全表会把连接池打满、拖慢正在播的那条
3. 语音要盖住 BGM → 靠基准音量差(配音 0.8 / BGM 0.6),不做压低联动
4. 页面不可见不念 → visibilitychange 隐藏时暂停当前句、队列挂起,
回前台从断点续播
第 3 条是把 DAY 15 的 ducking 改掉了 —— 之前朗读时压低 BGM,现在是"各播各的,靠音量差"。少一层联动,少一处 bug。
四首主题曲换新词
同一天,四首主题曲换用新词:
| 剧本 | 新曲名 |
|---|---|
| 李斯 | 《仓中鼠》 |
| 荆轲 | 《易水寒》 |
| 韩信 | 《拜将坛》 |
| 项羽 | 《不肯过江东》 |
李斯那首换得最彻底 —— 以仓鼠意象贯穿一生,首尾落于东门牵黄犬。
五剧本图标
新增 icon-<剧本>.png + icon-<剧本>-line.png(线稿版),进 h5/src/assets/icon/。


二、DAY 18:第三条音频通道 + 一个防软锁
音效系统(game/sfx.ts)
BGM 和朗读之外,补上交互反馈音。源由 Seed Audio 生成(mp3),经 ffmpeg 转 webm(opus)。
与 BGM/朗读的三点差异,写在头注里:
| BGM / 朗读 | 音效 | |
|---|---|---|
| 并发 | 单元素串行 | 并发短音:按名字开小池,池内轮询空闲元素,每名封顶 3 个 |
| visibility | 隐藏即暂停 | 不做暂停:全是 0.5–2.5s 的一次性音,让它自然响完比掐断更顺 |
| 音量 | 独立设置项 | 读 sfx/sfxGain(设置页 0–100 滑杆,0 即关) |
第 1 条是被需求逼出来的:音效可能连发(快速连点按钮)。单元素串行会互相打断,所以必须开池。
只出 webm(opus)一种 —— 2026-10-20 用户拍板:
原本另备一套 m4a 是为 Safari 兜底,目标平台 TapTap 容器的 WebView 支持 opus,不值得付双份体积。
音效清单:back / card / chapter / click / coin / dialog / dice-land / dice-roll / down / ending / enter / fail / gain / seal 等。
引擎:财富见底锁(2026-10-16 拍板)
做音效时顺手在引擎里补了一个防软锁。
问题:有些事件/选项全部都要扣财富。玩家财富归零后,点哪个都失败 —— 卡死在这条路径上。
修法:财富 ≤ 0 时,凡扣财选项一律锁定:
js复制
jsGame.prototype._caifuBroke = function () { return (this.attrs.caifu || 0) <= 0; }; Game.prototype.CAIFU_LOCK_REASON = '财富见底,无力为之';
三个层次都锁:
- UI 锁(展示层,灰显 + 理由)
- 引擎兜底(UI 锁只是展示层,引擎不依赖 UI 防御)
- 抉择卡同口径
有一条设计判断写得很清楚:
钱不够不是险招,直接锁 —— 险招是赌属性,支付能力没得赌。
还有一条防软锁的实测依据:
数据实测存在「全部选项都扣财富」的事件(陈胜 R-1、韩信 R-7)。
顺带修的一处记账 bug
js复制
js// 用 += 而非 =:同一章可能两次触顶(钳到 95 后又涨过 100), // 覆盖写会让第二次的回落从结算里消失。
以及章内状态归位:
钳制记账一并清零:回溯把 attrs 退回章首快照,那次回落已随 attrs 撤销。
不清的话,章末结算会再报一次玩家从未经历的危机下降。
“章内状态须归位” 这条规律,和 randomCount / _skipRandom 是同一个道理。


三、DAY 19:动效体系
先摸底,再动手
docs/动效优化规划-motion-design.md 是先规划、后落地的产物 —— 用 motion-design 技能对 h5/src 全量摸底后写出的清单。
摸底结论很直接:基础不差,缺体系。
| 位置 | 现状 | 问题 |
|---|---|---|
| 卡片入场 | 0.45s + stagger | 已达标;缺 secondary 层 |
| 险招骰点 | dice-shake(linear infinite) | linear 用错(线性只给 spinner) |
| 雷达图 | 数值变化时 points 瞬跳重绘 | 唯一「数据可视化无补间」硬伤 |
| 浮层 | hidden 直接 display:none | 弹窗瞬时出现/消失 |
| 页面切换 | 瞬时换页 | 无任何转场 |
| 主菜单环境 | 船行 210s / 浮云 drift | ambient 层教科书级,保留 |
四批全落地
| 批次 | 内容 |
|---|---|
| P0 token | --ease-ink/--ease-exit + --dur-quick/std/slow,全站 0.15/0.16/0.18s 过渡归拢 |
| 弹窗 | 入场 350ms 淡入+上浮;出场用「冻结克隆体」播 200ms 淡出 |
| 骰点重排 | 预备起势 + 缓摆 + squash 落定,去掉 linear |
| 朱印盖章 | ending__seal / seal--big |
| 雷达补间 | renderRadarTween(380ms ease-out-cubic,rAF 插值多边形/顶点/数值) |
| 页面转场 | page-in(插入即播,纯 CSS) |
| 逐字墨染 | .prose__ch opacity+blur 240ms |
| 卡片 secondary | 内容延迟 0.18s 跟进 |
| 云雾 ambient | 对局页 52s alternate,opacity 0.12 |
| meter 进度条 | width → scaleX(避免每帧重排) |
一处值得记的实现
弹窗出场用「冻结克隆体」:
出场用「冻结克隆体」播 200ms 淡出 —— hidden 语义同步,测试零改动
弹窗出场如果直接改原元素,会和 hidden 语义打架(测试依赖 hidden 判定)。所以克隆一个冻结的副本来播淡出,原元素照常 hidden —— 动效加了,测试零改动。
另一处:meter 进度条从 width 改成 scaleX —— 避免每帧重排。
印章抽取(data/seals.ts)
五印(循史/苟活/稳健/逆天/败局)原本私有在 ui/gallery.ts。DAY 19 结局/详情浮层顶端也要用印章图,于是抽到 data/:
抽到这里避免 game 层反向 import ui 层。
罗盘巡游测试(tests/wheel.cjs)
新增一套专门测罗盘空闲巡游:
加速参数(页内钩子):
- __QS_WHEEL_AUTO_IDLE__ = 800ms(默认 20000)
- __QS_WHEEL_AUTO_SPEED__ = 240°/s(默认 3°/s)
动线:主菜单 → 「开 始 游 戏」→ 选择页,之后不操作,等巡游自动选中。
归位后「明」离左上触发区最近(27°),第一个自动选中的必是它。
用页内钩子把 20 秒的等待压到 800ms —— 和 tapcloud 的上传间隔钩子是同一个手法。


四、三天的数据
| 项 | 值 |
|---|---|
| 配音重做 | 3854 条旧切片删除 → 1200 条新切片(整段合并口径) |
| 配音覆盖 | 李斯全剧本 134 条 |
| 主题曲 | 4 首换新词(李斯《仓中鼠》等) |
| 音效 | 14+ 条(assets/audio/sfx/) |
| 动效 | 四个优先级批次全落地 |
| 引擎 | 财富见底锁 + 记账 bug 修复(+55 行) |
| 新增模块 | game/sfx.ts / data/seals.ts / tests/wheel.cjs |
| 新增素材 | 五剧本图标(彩色+线稿)、上线封面、按钮方案三套 |
| 测试 | 15 套 |


五、三天最值钱的三条
① 配音"按段切"到"按整段合并",是节奏问题不是技术问题
旧方案技术上完全可行(每段一个文件,查表精确)。坏在听感上 —— 叙事文本是连贯的一句话,切成 4 段念,听起来就是断续的。
能跑通的技术方案,不一定是对的方案。 判断标准在玩家耳朵里,不在代码里。
② “钱不够不是险招,直接锁”
财富见底锁最有价值的一句注释:
险招是赌属性,支付能力没得赌。
同一个"锁定"的 UI 表现,背后可以是完全不同的机制。险招是概率,财富不足是确定性事实 —— 把确定性的东西做成概率(或反之),玩家立刻能感觉到不对劲。
③ 加动效时,测试零改动是可以做到的
弹窗出场用冻结克隆体播淡出,原元素照常 hidden —— 动效加了,依赖 hidden 语义的测试一行不用改。
"加效果"和"不动契约"不矛盾,前提是加效果时不改元素的语义状态。


明天
- 配音真机验收 —— 整段合并后的播放节奏、_shared 回落
- 音效真机验收 —— 连发场景(快速连点)的池化表现
- 上线素材 —— 封面已出(COVER_HORIZONTAL/VERTICAL),待补截图与宣传图
- 成就图标 64 缺失 —— 仍待 P3 美术批次
- voice.cjs 偶发红(单跑复绿,与动效无关)—— 待定位


载体:H5(Vite + TypeScript)· 状态:配音整段合并重做 + 音效系统 + 动效体系 + 财富见底锁 · 测试 15 套 · Lua 版同步中










