WAV——音效与音效库的关联方式
横扫千军的音效(单位语音、武器命中声、界面点击声)都是普通的 8 位 / 16 位 PCM WAV 文件——容器本身没有任何 TA 特有之处。有意思的是关联方式:单位 FBI、武器 TDF 和全局界面如何通过三层间接引用,把文件名送进声音总线。
Tip
自己试试。
# Play one directly
ls $(kbot ctx path)/sounds/ | head
afplay $(kbot ctx path)/sounds/cmdsel1.wav # macOS
aplay $(kbot ctx path)/sounds/cmdsel1.wav # Linux
# The web UI also previews WAV/MP3 inline
kbot mount $(kbot ctx path) --server
磁盘格式:就是 WAV
WAV 文件放在 sounds/ 中。它们遵循标准的 RIFF WAVE 格式——任何能播放 WAV 的程序都能播放它们。kbot 不把它们当作自定义格式解析;资源浏览器把它们交给浏览器的 <audio> 元素处理。
Cavedog 发行文件的典型编码:
| 属性 | 值 |
|---|---|
| 采样率 | 22050 Hz(大多数),11025 Hz(部分界面点击声) |
| 位深 | 8 位无符号 PCM |
| 声道 | 单声道 |
| 时长 | 0.1–3 秒 |
| 文件大小 | 5 KB – 200 KB |
模组偶尔会发布 16 位立体声的采样;引擎接受它们,但会占用更多内存。
三层关联
查找“选中 Arm 指挥官时播放什么”,要沿着这条链走:
FBI gamedata/sound.tdf sounds/
┌─────────────┐ ┌───────────────────────┐ ┌──────────────┐
│ units/ │ │ [ARM_COM] │ │ cmdsel1.wav │
│ armcom.fbi │ │ { │ │ │
│ │ ──▶ │ select1=cmdsel1; │ ──▶│ │
│ Sound │ │ ok1=cmdok1; │ │ │
│ Category= │ │ ... │ │ │
│ ARM_COM; │ │ } │ │ │
└─────────────┘ └───────────────────────┘ └──────────────┘
1. FBI picks 2. sound.tdf maps 3. WAV file
a category event → filename on disk
第 1 层——FBI 中的 SoundCategory
单位的 FBI 指定一个类别:
[UNITINFO]
{
UnitName=ARMCOM;
SoundCategory=ARM_COM; // ← points at [ARM_COM] in sound.tdf
...
}
类别是多个单位共用的——每个 Arm 指挥官变体,或者每个 Arm K-bot,都指向同一个类别,这样同一阵营内的选中语音就能保持一致。
第 2 层——gamedata/sound.tdf
这个 TDF 在一个类别内把引擎事件绑定到 WAV 文件名:
[ARM_COM]
{
select1=cmdsel1; // Selecting the unit
ok1=cmdok1; // Movement order acknowledged
arrived1=cmdarvd1; // Reached destination
cant1=cantdo4; // Order rejected ("I can't do that")
underattack=warning1; // Took damage
count5=count1; // Commander self-destruct countdown (5)
count0=count6; // …countdown (now)
canceldestruct=cancel2;
}
注意大多数事件名都带有 1 后缀。每个事件都可以有一组候选:不带编号的键(select)和带编号的键 select1、select2、select3……;引擎会随机挑选一个,以增加变化。如果只定义了 select1,每次播放的都是同一句。
对于每个事件,TA 3.1c 先读取不带编号的键(如果有),然后依次读取 KEY1、KEY2……,直到第一个缺失的编号为止:写成 select1=kbarmsel; select3=kbarmse2; 时,只会播放 kbarmsel,因为缺失的 select2 结束了这个列表。(缺少不带编号的键并不会阻止带编号的键。)空值是一个无声的候选。不属于下列事件的键永远不会被播放。
第 3 层——sounds/<name>.wav
TDF 中的值是词干(不带 .wav 扩展名)。引擎在虚拟文件系统中查找 sounds/<stem>.wav。
Important
.wav 后缀是隐含的。 如果你在 TDF 中写 select1=cmdsel1.wav,引擎会去找 sounds/cmdsel1.wav.wav,然后悄无声息地什么也不播放。
标准事件键
TA 3.1c 从单位的音效类别中读取 23 个事件,每个事件都是不带编号的键加上上文所述的一组带编号的键(ok、ok1、ok2……):
| 事件 | 触发时机 |
|---|---|
select |
单位被选中。 |
underattack |
单位受到伤害。 |
activate / deactivate |
开/关切换(雷达、干扰器、防御工事)。 |
ok |
命令被接受。 |
arrived |
单位完成一次移动。 |
cant |
命令被拒绝(地形、建造队列等原因)。 |
unitcomplete |
单位建造完成。 |
build |
建造者开始建造。 |
repair |
建造者开始修理。 |
working |
建造者正在回收/复活。 |
load / unload |
运输单位装载 / 卸下一个单位。 |
cloak / uncloak |
单位隐形 / 解除隐形(指挥官)。 |
capture |
指挥官或能够俘获的单位开始俘获。 |
count5 … count0 |
自毁倒计时的每一跳。 |
canceldestruct |
自毁被取消。 |
每个键都可以有一个对应的 KEYtext 条目(select1text),存放这句台词的字幕。类别中的其他键会被忽略。
KBot Studio 的单位查看器和沙盒遵循同样的规则:单位元数据(/api/studio/unit/{name})恰好携带游戏在这些事件中会播放的那些键,每个事件从它的键中随机播放一个。
界面音效——allsound.tdf
界面事件(按钮点击、菜单切换、对局结束的提示音)的键的写法相同,但放在 gamedata/allsound.tdf 中 [GLOBAL_*] 风格的节里:
[BIGBUTTON] { sound=butmain1; }
[SMLBUTTON] { sound=button1; }
[SKIRMISH] { sound=butnskir; }
[BGM] { sound=drone2; } // Background music ID
这些节由菜单 .gui 文件按名称引用。要更改“Skirmish”按钮的点击声,只需要编辑这个 TDF——不用改 .gui。
武器音效
武器 TDF(TDF)直接携带自己的音效键,没有中间的间接层:
[LIGHTLASER] {
...
soundstart=lasrmas1; // On fire
soundhit=lasrhit1; // On impact
soundwater=splshbig; // On water hit (overrides soundhit)
soundtrigger=1; // Play soundstart on each burst pulse, not just first
}
同样适用 sounds/<stem>.wav 的查找规则。武器音效按每一发调校——如果你更换了单位的武器,也需要在武器 TDF 中重新调校音量。
实例解析——添加一句新的选中语音
把一个新的 WAV 放进
sounds/(建议单声道、22050 Hz、8 位):mymod/sounds/cmdsel_grunt.wav在
gamedata/sound.tdf中添加一个新事件:[ARM_COM] { select1=cmdsel1; // Existing select2=cmdsel_grunt; // Your new line, randomly selected }不需要修改 FBI——现有的
SoundCategory=ARM_COM引用就够了。打包并测试:
kbot hpi pack ./mymod --target mymod.ufo
现在选中指挥官时,引擎会在 cmdsel1.wav 和 cmdsel_grunt.wav 之间轮换。
典型大小
| 资源 | 在 Cavedog 资源中观察到的范围 |
|---|---|
| 单位语音 | 5–60 KB |
| 武器命中声 | 5–30 KB |
| 爆炸声 | 30–120 KB |
| 音乐循环 | 100 KB – 1 MB |
gamedata/sound.tdf |
约 20 KB(300 多个类别) |
gamedata/allsound.tdf |
约 6 KB |
坑点
Warning
缺失的 WAV 只会无声,不会报错。 把 select1=cmsdel1 拼错(字母颠倒),引擎在选中单位时就只是什么也不播放——没有日志消息,也没有后备。改过的音效一定要在游戏中测试。
.wav扩展名是隐含的——绝不要把它写进 TDF 的值中。- 采样率很重要。 采样率不是 8 kHz / 11.025 kHz / 22.05 kHz / 44.1 kHz 的声音,在某些零售版引擎仍在使用的 Windows 9x 时代硬件路径上可能会卡顿。22050 Hz 单声道是稳妥的默认选择。
- 立体声 WAV 会被引擎混缩为单声道。 除非有特定的空间定位理由,否则不要发布立体声。
sounds/中的 MP3——引擎会忽略这里的 MP3。音乐播放器对music/*.mp3有单独的代码路径。- Cavedog 发布的有些 WAV 是 16 位 PCM,采样率也很古怪(例如 7000 Hz)——它们照样能用,但如果你编写的工具假定是 22050/8/单声道,就会在它们身上栽跟头。