FMOD 与音频
本文档说明游戏的音频架构,以及 RitsuLib 在此基础上提供的分层 API。
游戏原版音频架构
杀戮尖塔 2 通过 Godot 的 FMOD Studio GDExtension(FmodServer 单例)播放音频。C# 侧由 NAudioManager 封装,它通过 GDScript 代理 AudioManagerProxy 间接调用 FmodServer。
这意味着:
- 原版的音频播放最终都经过
NAudioManager→AudioManagerProxy→FmodServer这条路径 NAudioManager包含TestMode静音、SFX 音量施加等行为- 如果 Mod 希望音频行为"听起来和原版一样",应该走同一条管线
RitsuLib 音频 API
RitsuLib 将音频 API 分层,既能走与原版一致的管线,也能在需要时直连 FMOD Studio。
入口选择
| 需求 | 使用 |
|---|---|
| 更易用的高层播放、返回 handle、自动生命周期清理 | GameFmod.Playback |
与原版相同的路由 / TestMode 行为 | GameFmod.Studio → NAudioManager |
与 SfxCmd 相同的防护(非交互、战斗结束等) | Sts2SfxAlignedFmod |
| 加载/卸载 Studio Bank、检查路径 | FmodStudioServer |
在 FmodServer 上直接 one-shot(不经过 NAudioManager) | FmodStudioDirectOneShots |
| Bus 音量/静音/暂停、全局参数、DSP、性能数据 | FmodStudioBusAccess、FmodStudioMixerGlobals |
Snapshot(snapshot:/…) | FmodStudioSnapshots |
长期持有的 create_event_instance | FmodStudioEventInstances |
| 通过插件加载 WAV/OGG/MP3 | FmodStudioStreamingFiles |
| 冷却、随机池(本身不发声) | FmodPlaybackThrottle、FmodPathRoundRobinPool |
直连 FMOD 与原版管线的区别
GameFmod.Studio和Sts2SfxAlignedFmod走NAudioManager,与原版游戏共享 GDScript 代理(含TestMode、SFX 音量等)FmodStudioDirectOneShots及多数FmodStudio*直接调用FmodServer,适合自定义 Bank、散文件、Bus 调试- 如果要"听起来和原版一样",优先使用
GameFmod或Sts2SfxAlignedFmod
简短示例
与原版一致的 one-shot
csharp
using STS2RitsuLib.Audio;
Sts2SfxAlignedFmod.PlayOneShot("event:/sfx/heal");
GameFmod.Studio.PlayMusic("event:/music/menu_update");模组内容 Bank + guids.txt
csharp
FmodStudioServer.TryLoadBank("res://mods/MyMod/banks/MyMod.bank");
FmodStudioServer.TryWaitForAllLoads();
if (!FmodStudioServer.TryLoadStudioGuidMappings("res://mods/MyMod/banks/MyMod.guids.txt"))
return;
if (FmodStudioServer.TryCheckEventPath("event:/mods/mymod/hit") is true)
GameFmod.Studio.PlayOneShot("event:/mods/mymod/hit");流式音乐(推荐:新 Playback/Handle API)
csharp
var musicPath = ProjectSettings.GlobalizePath("user://mymod/loop.ogg");
var handle = GameFmod.Playback.PlayMusic(
AudioSource.StreamingMusic(musicPath),
new AudioPlaybackOptions { Volume = 0.7f, Scope = AudioLifecycleScope.Room }
);跟随游戏自动切换的常见三段式音乐(房间 / 战斗 / 胜利)
csharp
var adaptive = GameFmod.Playback.FollowAdaptiveMusic(
AudioAdaptivePlans.FullRunOverride(
roomSource: AudioSource.StreamingMusic(roomLoopPath),
combatSource: AudioSource.StreamingMusic(combatLoopPath),
victorySource: AudioSource.StreamingMusic(victoryStingerPath)
)
);单例频道:替换当前播放
csharp
GameFmod.Playback.PlayMusic(
AudioSource.StreamingMusic(nextMusicPath),
new AudioPlaybackOptions
{
Volume = 0.8f,
Routing = new AudioRoutingOptions
{
Channel = "my-mod/music",
ChannelMode = AudioChannelMode.ReplaceExisting,
AllowFadeOutOnReplace = true,
},
}
);辅助类型(STS2RitsuLib.Audio)
| 类型 | 说明 |
|---|---|
FmodEventPath | event:/… 路径轻量封装 |
FmodStudioRouting | 常用 Bus 路径常量 |
FmodParameterMap | 为 GameFmod.Studio 构造参数字典 |
模组附加 Bank 制作(推荐流程)
1. Bank 类型与命名
- 不要替换或改名覆盖游戏自带的
Master.bank。 - 模组应使用 独立命名的内容 Bank,文件名与 Studio 内 Bank 名称在游戏内全局 唯一。
- 该 Bank 仅承载你的 Event / 采样;混音树上的 Master / Routing 仍依赖游戏已加载的原版 Master 管线。
2. Bus / Master 对齐(与原版混音一致)
- 游戏里
AudioManagerProxy使用的典型路径包括bus:/master、bus:/master/sfx、bus:/master/music、bus:/master/ambience。 - 若希望模组音效/音乐的分轨、衰减、音量滑条行为与原版一致:在 FMOD Studio 中为 Event 指定的 Routing / Output,应落到上述 已与原版一致的
bus:/…路径上。
3. 导出 GUID 并导入模组资源
- 在 FMOD Studio 中 Build 你的模组 Bank。
- 在生成目录中取
GUIDs.txt。 - 拷贝为模组中的文本资源。
- 游戏初始化时调用
FmodStudioServer.TryLoadStudioGuidMappings("..."):框架会写入路径 → GUID 表。
4. 运行时顺序与稳定性
- 在游戏的 FMOD 启动流程与
NAudioManager已就绪之后再TryLoadBank。 - 使用
FmodStudioServer.TryLoadBank加载模组 Bank:实现会 保留返回的FmodBank引用。
5. 版本与产物一致性
- FMOD Studio 主版本须与游戏内
addons/fmod所用库一致。 - 同一次 Build 产出的
.bank与GUIDs.txt必须成对发布。
排错
FmodStudioServer.TryGet()为 null —FmodServer未就绪,查游戏日志TryCheckEventPath为 false — 对应.bank未加载、路径写错、TryLoadStudioGuidMappings未成功,或 Bank 已被卸载- 无声且无异常 —
TestMode/NonInteractiveMode可能抑制NAudioManager;直连FmodServer不受这些标志约束