所有格式页面 ↓
图形与媒体

WAV——音效与音效库的关联方式

来自 coreprime/kbot @ 0ab97bf · 在 GitHub 上编辑

横扫千军的音效(单位语音、武器命中声、界面点击声)都是普通的 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 中重新调校音量。


实例解析——添加一句新的选中语音

  1. 把一个新的 WAV 放进 sounds/(建议单声道、22050 Hz、8 位):

    mymod/sounds/cmdsel_grunt.wav
  2. 在 gamedata/sound.tdf 中添加一个新事件:

    [ARM_COM]
    {
        select1=cmdsel1;       // Existing
        select2=cmdsel_grunt;  // Your new line, randomly selected
    }
  3. 不需要修改 FBI——现有的 SoundCategory=ARM_COM 引用就够了。

  4. 打包并测试:

    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/单声道,就会在它们身上栽跟头。

另见

  • TDF——sound.tdf、allsound.tdf 和武器音效字段所用的格式。
  • 横扫千军:王国界面——.gui 控件引用界面音效事件。
  • 术语表——音效类别(sound category)。