Skip to content

FMOD 与音频

本文档说明游戏的音频架构,以及 RitsuLib 在此基础上提供的分层 API。

游戏原版音频架构

杀戮尖塔 2 通过 Godot 的 FMOD Studio GDExtensionFmodServer 单例)播放音频。C# 侧由 NAudioManager 封装,它通过 GDScript 代理 AudioManagerProxy 间接调用 FmodServer

这意味着:

  • 原版的音频播放最终都经过 NAudioManagerAudioManagerProxyFmodServer 这条路径
  • NAudioManager 包含 TestMode 静音、SFX 音量施加等行为
  • 如果 Mod 希望音频行为"听起来和原版一样",应该走同一条管线

RitsuLib 音频 API

RitsuLib 将音频 API 分层,既能走与原版一致的管线,也能在需要时直连 FMOD Studio。

入口选择

需求使用
更易用的高层播放、返回 handle、自动生命周期清理GameFmod.Playback
与原版相同的路由 / TestMode 行为GameFmod.StudioNAudioManager
SfxCmd 相同的防护(非交互、战斗结束等)Sts2SfxAlignedFmod
加载/卸载 Studio Bank、检查路径FmodStudioServer
FmodServer 上直接 one-shot(不经过 NAudioManagerFmodStudioDirectOneShots
Bus 音量/静音/暂停、全局参数、DSP、性能数据FmodStudioBusAccessFmodStudioMixerGlobals
Snapshot(snapshot:/…FmodStudioSnapshots
长期持有的 create_event_instanceFmodStudioEventInstances
通过插件加载 WAV/OGG/MP3FmodStudioStreamingFiles
冷却、随机池(本身不发声)FmodPlaybackThrottleFmodPathRoundRobinPool

直连 FMOD 与原版管线的区别

  • GameFmod.StudioSts2SfxAlignedFmodNAudioManager,与原版游戏共享 GDScript 代理(含 TestMode、SFX 音量等)
  • FmodStudioDirectOneShots 及多数 FmodStudio* 直接调用 FmodServer,适合自定义 Bank、散文件、Bus 调试
  • 如果要"听起来和原版一样",优先使用 GameFmodSts2SfxAlignedFmod

简短示例

与原版一致的 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

类型说明
FmodEventPathevent:/… 路径轻量封装
FmodStudioRouting常用 Bus 路径常量
FmodParameterMapGameFmod.Studio 构造参数字典

模组附加 Bank 制作(推荐流程)

1. Bank 类型与命名

  • 不要替换或改名覆盖游戏自带的 Master.bank
  • 模组应使用 独立命名的内容 Bank,文件名与 Studio 内 Bank 名称在游戏内全局 唯一
  • 该 Bank 仅承载你的 Event / 采样;混音树上的 Master / Routing 仍依赖游戏已加载的原版 Master 管线。

2. Bus / Master 对齐(与原版混音一致)

  • 游戏里 AudioManagerProxy 使用的典型路径包括 bus:/masterbus:/master/sfxbus:/master/musicbus:/master/ambience
  • 若希望模组音效/音乐的分轨、衰减、音量滑条行为与原版一致:在 FMOD Studio 中为 Event 指定的 Routing / Output,应落到上述 已与原版一致的 bus:/… 路径上。

3. 导出 GUID 并导入模组资源

  1. 在 FMOD Studio 中 Build 你的模组 Bank。
  2. 在生成目录中取 GUIDs.txt
  3. 拷贝为模组中的文本资源。
  4. 游戏初始化时调用 FmodStudioServer.TryLoadStudioGuidMappings("..."):框架会写入路径 → GUID 表。

4. 运行时顺序与稳定性

  • 在游戏的 FMOD 启动流程与 NAudioManager 已就绪之后再 TryLoadBank
  • 使用 FmodStudioServer.TryLoadBank 加载模组 Bank:实现会 保留返回的 FmodBank 引用

5. 版本与产物一致性

  • FMOD Studio 主版本须与游戏内 addons/fmod 所用库一致。
  • 同一次 Build 产出的 .bankGUIDs.txt 必须成对发布。

排错

  • FmodStudioServer.TryGet() 为 nullFmodServer 未就绪,查游戏日志
  • TryCheckEventPath 为 false — 对应 .bank 未加载、路径写错、TryLoadStudioGuidMappings 未成功,或 Bank 已被卸载
  • 无声且无异常TestMode / NonInteractiveMode 可能抑制 NAudioManager;直连 FmodServer 不受这些标志约束