UE5.8 音频系统实战:Sound Mix 与 Snapshot

📑 目录

UE5.8 音频系统实战:Sound Mix 与 Snapshot

小林第一次负责项目音频混音时,犯了一个特别典型的错误:他把所有 Sound Cue 的 Volume 都调到了自以为合适的数值,BGM 是 0.6,枪声是 0.8,脚步声是 1.0。演示那天,策划在山谷里放了一颗手雷,爆炸声如期响起,背景音乐也还在没心没肺地响着。整个场景像一个所有乐器都在抢麦的乐队。

那场 demo 之后,主策只跟他说了一句话:爆炸的时候,音乐要让开。

这句话就是 Sound Mix 的起点。

一个爆炸场景的混音诉求

回到那个山谷。手雷爆炸时,我们真正想听到的信息有三层:

  • 爆炸本身的冲击感,包括低频轰鸣与高频破片声;
  • 角色的受击语音,告诉玩家他受伤了;
  • 环境音和背景音乐暂时退后,给前两层腾出空间。

如果靠手动调每个 Sound Cue 的 Volume,一辈子都做不完。你不可能预先知道每个爆炸发生的位置、距离、当前 BGM 播到哪一段、玩家是否正踩在草地上。混音必须动态发生,依据游戏状态实时计算。

UE5.8 提供了两条主线来解决这件事:

  • Sound Mix:按 Sound Class 对一组声音做动态的 Volume、Pitch、EQ 等参数调整;
  • Snapshot:在特定时刻把整个音频系统的状态拍下来并应用,通常用于环境切换或特殊听感。

理解它们之前,得先搞清楚 Sound Class 是怎么回事。

Sound Class 层级:先给声音分类

Sound Class 是 UE 音频系统里的分类器。每个 Sound Wave 或 Sound Cue 都可以指定一个 Sound Class,比如 SFXMusicVoiceAmbient。编辑器里点开 Content Browser 的声音资源,在 Details 面板的 Sound 分类下就能找到这个字段。

真正有用的地方在于,Sound Class 支持层级。你可以建一个 Master 在最顶层,下面分出 MusicSFXVoiceAmbientSFX 下面再细分 WeaponsFootstepsExplosions。当父类被压低声级时,子类会跟着一起受影响。

flowchart TD
    subgraph master ["Master 总线"]
    end
    subgraph music ["Music"]
        bgm["BGM 背景音乐"]
        stinger["Stinger 过渡音乐"]
    end
    subgraph sfx ["SFX"]
        ui["UI 音效"]
        weapon["Weapon 武器"]
        foot["Footstep 脚步"]
        expl["Explosion 爆炸"]
    end
    subgraph voice ["Voice"]
        dialog["Dialog 对白"]
        shout["Shout 喊叫"]
    end
    subgraph ambient ["Ambient"]
        wind["Wind 风声"]
        rain["Rain 雨声"]
    end
    master --> music
    master --> sfx
    master --> voice
    master --> ambient

这里有一个容易踩的坑:很多人会想把所有声音都塞进一个扁平的 Sound Class 列表,觉得分类越多越专业。实际做的时候你会发现,层级太深会让调试变成灾难。我建议在项目早期就定好四类:Music、Voice、SFX、Ambient,武器、爆炸、UI 这些再按需求挂到 SFX 下面。四层足够应付绝大多数项目。

Sound Class 自身也带有一组属性,比如 VolumePitchStereo BleedReverbLPF FrequencyAttenuation Distance Scale。这些属性会被子类继承,也可以在运行时被 Sound Mix 覆盖。

实际建资源的时候,我会先建一个 SC_Master 作为根节点,把所有具体分类挂上去。项目早期不要把 Volume 默认值填得太满,留一点余地给 Mix。比如你默认把 BGM 的 Sound Class Volume 设成 0.7,后期如果某个关卡需要整体再降,可以直接在 Mix 里用 -3 dB 左右的空间。全部设成 1.0,等于把调音台推子一开始就推到顶,后面想压只能硬切。

Sound Mix:动态混音的核心

Sound Mix 本质上是一份调音台预设。它定义了针对哪些 Sound Class 做什么样的调整。每个 Sound Mix 包含一个 Sound Mix Class Overrides 数组,每一项指定一个 Sound Class,以及要修改的 Volume、Pitch、Voice Center Channel Volume、LPF、HPF 等参数。

那个山谷爆炸场景,我们可以建一个 ExplosionDuckMix,让它压低 Music 和 Ambient 的 Volume,同时给 SFX 加一点点低频增益。当手雷爆炸时,调用 Push Sound Mix Modifier 把这个 Mix 压入音频系统;爆炸结束后,调用 Pop Sound Mix Modifier 把它弹出来。

flowchart TD
    A["手雷触发 Explosion Cue"] --> B["Push ExplosionDuckMix"]
    B --> C["Music Volume -6 dB"]
    B --> D["Ambient Volume -9 dB"]
    B --> E["SFX LPF 打开 保留冲击"]
    F["爆炸尾音结束"] --> G["Pop ExplosionDuckMix"]
    G --> H["音乐与环境恢复"]

Sound Mix 的优先级很重要。UE 允许同时存在多个 Active Sound Mix,后压入的不一定覆盖前面的,而是看它们的 Priority 与 Apply Volume。Priority 越大越优先,Apply Volume 决定该 Mix 对最终结果的权重。这个设计让多个 Mix 可以叠加,而不是粗暴地互相顶替。

我给项目里的 Mix 定了一套优先级约定:全局环境 Snapshot 用 1.0,战斗状态 Mix 用 2.0,爆炸 Duck 用 3.0,语音优先用 4.0,过场动画强制对白用 5.0。数字越大越能盖过其他 Mix。Apply Volume 我一般默认 1.0,只有需要柔和叠加时才降到 0.5 或 0.3。优先级没约定清楚的话,很容易出现语音 Mix 被战斗 Mix 压掉的情况,排查起来很头疼。

比如你在爆炸的同时进入了一个水下区域,水下 Mix 和爆炸 Duck Mix 会同时生效。最终结果不是谁音量更大,而是按权重混合后的数值。

数学上,如果两个 Mix 对同一个 Sound Class 的 Volume 调整分别是 V1V_1V2V_2(以线性倍数表示),UE 的混合结果大致可以看成加权乘积:

Vfinal=VbaseV1w1V2w2V_{final} = V_{base} \cdot V_1^{w_1} \cdot V_2^{w_2}

其中 w1w_1w2w_2 是各自 Mix 的 Apply Volume,取值在 0011 之间。把这个式子转成 dB:

Lfinal (dB)=20log10(Vbase)+20w1log10(V1)+20w2log10(V2)L_{final\ (dB)} = 20 \log_{10}(V_{base}) + 20 w_1 \log_{10}(V_1) + 20 w_2 \log_{10}(V_2)

实际调音时,人耳对 dB 更敏感。把 6 dB-6\text{ dB} 对应的线性值记为 0.50.512 dB-12\text{ dB} 对应 0.250.25,这样心算会快很多。

Snapshot:拍一张照片,然后切换整个听感

Snapshot 是 UE 音频系统的另一个利器。如果说 Sound Mix 是针对分类做参数调整,Snapshot 就是把整个音频引擎的状态整体切换。它最常用于环境变化:角色钻进水里、戴上防毒面具、进入洞穴、被 EMP 干扰。

去年我们做一个潜水关卡,美术把水下场景做得特别漂亮,但声音一直不对。玩家跳进水里之后,脚步声、枪声、背景音乐还和陆地上一样清脆。我加了一个 UnderwaterSnapshot,里面重新指定了所有 Sound Class 的 LPF、HPF、Reverb 和 Volume。玩家入水瞬间触发这个 Snapshot,出水上岸再切回默认 Snapshot。

Snapshot 的实现方式有几种:

  • 在 Sound Cue 或 Blueprint 里直接调用 Set Sound Mix Class Override
  • Audio Volume 的 Reverb 与 Ambient Zone 设置;
  • 在 C++ 里通过 FAudioDevice 管理 Snapshot。
graph LR
    A["陆地默认 Snapshot"] -->|"跳入水中"| B["水下 Snapshot"]
    B -->|"浮出水面"| A
    B -->|"进入洞穴"| C["洞穴 Snapshot"]
    C -->|"返回水下"| B

Snapshot 与 Sound Mix 的关系容易搞混。你可以把 Snapshot 理解成一种更重量级的 Mix。Sound Mix 通常由短事件触发,比如爆炸、开枪、受击;Snapshot 通常由持续状态触发,比如在水里、在车里、在暴风雪中。一个项目里,Snapshot 的数量应该远少于 Sound Mix。

Audio Volume 是 Snapshot 的好搭档。把 Audio Volume 放到水下区域,设置 Reverb 和 Ambient Zone,再绑定一个 Snapshot,玩家游进去就会自动切换。比在每个水体的 Blueprint 里写 BeginOverlap 要干净很多。Ambient Zone 会让区域外的声音听起来被隔离,配合 LPF cutoff 调低,水下那种闷闷的感觉立刻就出来了。

有个细节要注意:Snapshot 切换时如果直接硬切,玩家会听到明显的跳变。UE 的 Fade In TimeFade Out Time 可以缓解这个问题。水下场景我一般给 0.30.3 秒淡入、0.50.5 秒淡出,这样既自然又不会让玩家觉得是 bug。

Ducking:侧链压缩让关键声音冒出来

Ducking 是 Sound Mix 最常见的用法。它的思路来自硬件调音台上的侧链压缩:当 A 声音出现时,自动压低 B 声音的音量。我们前面说的爆炸压音乐,就是一种 Ducking。

在 UE5.8 里,Ducking 可以通过 Blueprint 手动触发,也可以在 Sound Cue 里用 ModulatorMixer 实现更简单的版本。但对于复杂的项目,我建议用 Sound Mix + Gameplay Tag 或 Gameplay Ability System 来管理。

sequenceDiagram
    participant Player as "玩家"
    participant Grenade as "手雷"
    participant Audio as "音频系统"
    participant Music as "Music 总线"
    participant SFX as "SFX 总线"

    Player->>Grenade: 投掷
    Grenade->>Audio: 触发 Explosion Cue
    Audio->>Music: Push Duck Mix
    Music-->>Music: Volume 0 dB -> -9 dB
    Audio->>SFX: 播放爆炸声
    SFX-->>SFX: 保持或略增强
    Grenade->>Audio: 爆炸尾音结束
    Audio->>Music: Pop Duck Mix
    Music-->>Music: Volume 恢复

Ducking 的参数设置需要反复试听。我常用的一个经验是:音乐压 6-69 dB-9\text{ dB},环境音压 9-912 dB-12\text{ dB},持续时间比关键声音长 200200500500 毫秒。压得太少,玩家感觉不到;压得太多,音乐会像被掐断一样难受。

触发时机也有讲究。爆炸 Duck Mix 应该在爆炸声播放前一点点就 Push 进去,而不是等爆炸已经开始。延迟哪怕 50 毫秒,玩家都会先听到一瞬完整的 BGM,再听到音乐被压下去,这种跳变很廉价。我通常把 Push 节点放在爆炸视觉特效生成的同时,而不是放在音效播放节点之后。

衰减曲线也很重要。线性淡出往往很生硬,我用 Sound Mix 里的 Fade Curve 设为对数型,让音乐先快后慢地回来。这个曲线可以用一个简单的幂函数近似:

f(t)=tαf(t) = t^\alpha

tt 是归一化时间,α\alpha 通常取 1.51.52.02.0α=1\alpha=1 是线性,α=2\alpha=2 是缓入缓出的感觉。

语音优先:让队友的声音不被枪声盖住

多人项目里,语音优先比 Ducking 更复杂。你要保证队友喊的“左边有人”能清晰传出来,但又不能把枪声完全掐掉,否则玩家会失去空间信息。

我们当时的方案是三层:

  • 队友语音触发 VoicePriorityMix,把 Music 压 6 dB-6\text{ dB},Ambient 压 4 dB-4\text{ dB}
  • 同时把 SFX 里的 Weapons 子类压 3 dB-3\text{ dB},但 ExplosionsFootsteps 不动;
  • 语音本身的 Sound Class Voice 保持原音量,并把 Voice Center Channel Volume 提高 2 dB2\text{ dB},让它在中置声道更突出。

这个方案的要点是只压会遮挡语音的那些声音,而不是一刀切。你可以把 SFX 的层级建得细一点,才能在 Mix 里做这种精准手术。

flowchart TD
    subgraph voice ["Voice 总线"]
    end
    subgraph music ["Music 总线"]
    end
    subgraph sfx ["SFX 总线"]
    end
    subgraph ambient ["Ambient 总线"]
        dialog["队友语音"]
        bgm["背景音乐"]
        weapon["枪声"]
        explosion["爆炸"]
        foot["脚步"]
        wind["风声"]
    end
    voice --> dialog
    music --> bgm
    sfx --> weapon
    sfx --> explosion
    sfx --> foot
    ambient --> wind

语音还有一个容易被忽略的点:当多个队友同时说话时,音量会叠加,反而什么都听不清。UE5.8 的 Voice Center Channel VolumeStereo Bleed 可以控制语音在声道中的定位,但更彻底的做法是在Gameplay层做语音排队或优先级过滤。这个已经超出纯音频系统的范畴,但混音设计师必须知道它的存在。

另一个细节是语音触发频率。如果玩家连续中弹,受击语音会一句接一句地触发,每次都 Duck 一下音乐和枪声,听起来会很碎。我们会在 Gameplay 代码里给同类语音加 2 到 3 秒的最小间隔,或者直接覆盖上一条还没播完的受击语音。

混音推子与 EQ:别只调音量

很多人一提到混音就只想到 Volume。其实 EQ 和滤波器才是让声音分层的关键工具。Sound Mix 里可以调整 LPF FrequencyHPF Frequency,也就是低通和高通滤波器。

爆炸时我通常会给 BGM 加一个 LPF,让音乐变得沉闷,而不是单纯变小。这样玩家会感觉到爆炸的冲击力,同时音乐还在背景里维持情绪。水下 Snapshot 则同时加 LPF 和 HPF,把中频保留、高低频切掉,模拟水对声波的过滤效果。

EQ 的截止频率选择有一些常用参考:

  • 爆炸 Ducking 的 LPF:400400800 Hz800\text{ Hz}
  • 水下 Snapshot 的 LPF:600 Hz600\text{ Hz},HPF:150 Hz150\text{ Hz}
  • 对讲机语音的 HPF:300 Hz300\text{ Hz},LPF:4000 Hz4000\text{ Hz}

这些数值不是死的,要靠耳朵调。实际调的时候我会先在编辑器里试听,然后打包到 devkit 上再听一遍。编辑器监听耳机和主机 TV 出来的低频完全不一样,同样在编辑器里觉得合适的 600 Hz cutoff,到 TV 上可能变成 800 Hz 才够闷。

除了 LPF 和 HPF,Sound Mix 还能调 Voice Center Channel Volume。这个参数在做 5.1 环绕声时特别有用,它控制语音在中置声道的占比。中置拉满,语音会从屏幕正前方出来;Stereo Bleed 拉高,语音会散到左右声道。过场动画一般中置给高,战斗中的队友语音我会让一点 stereo bleed,保持方向感。

UE 的 Sound Mix Debug 控制台命令可以实时看到当前激活的 Mix 和每个 Sound Class 的参数值。我常用的命令是:

au.SoundMix.Debug 1
au.DumpActiveSoundMixes

第一条会在屏幕上显示当前激活的 Sound Mix 列表,第二条会把详情打到日志里。调试混音问题时,这两条命令能省很多时间。

与 Gameplay 状态联动

混音不能只靠音频设计师在编辑器里调,它必须和游戏逻辑绑定。最常见的联动点包括:

  • 玩家生命值:低血量时压低 Music、提高 Heartbeat 音量;
  • 角色状态:奔跑、潜行、载具、水下;
  • 战斗状态:进入战斗触发 CombatMix,退出战斗 55 秒后恢复;
  • 剧情事件:过场动画时压低 SFX,突出对白。

我们项目里用 Gameplay Ability System 管理这些状态。每个 Ability 在激活和结束时分别 Push 和 Pop 对应的 Sound Mix。比如潜水 Ability 激活时 Push UnderwaterMix,结束时 Pop。这样混音逻辑和玩法状态天然同步,不用写一堆散落的 Blueprint 节点。

不用 GAS 的项目也可以在 Character 或 Player Controller 里维护一个 Active Mix 列表。进入状态时 Push,离开状态时 Pop。关键是要保证成对出现,否则 Mix 会残留在音频系统里,导致后面的关卡声音越来越怪。我们曾经遇到过一个 bug:玩家死亡时没有正确 Pop 战斗 Mix,复活后 BGM 一直比正常小。查了半天才发现是 Ability 的 End 回调没触发。

下面是一个简化的 C++ 示例,在角色进入水下时触发 Snapshot:

void AMyCharacter::EnterWater()
{
    if (USoundBase* UnderwaterSnapshot = LoadObject<USoundBase>(
        nullptr, TEXT("/Game/Audio/Snapshots/UnderwaterSnapshot.UnderwaterSnapshot")))
    {
        UGameplayStatics::SetSoundMixClassOverride(
            this,
            UnderwaterSnapshot,
            MasterSoundClass,
            1.0f,   // Volume
            1.0f,   // Pitch
            0.3f    // Fade in time
        );
        UGameplayStatics::PushSoundMixModifier(this, UnderwaterSnapshot);
    }
}

void AMyCharacter::ExitWater()
{
    if (ActiveSnapshot)
    {
        UGameplayStatics::PopSoundMixModifier(this, ActiveSnapshot);
    }
}

这个示例不到五十行,核心思想是:Gameplay 状态变化驱动混音变化,而不是反过来。代码里硬编码路径只是为了说明,真实项目应该用 TSoftObjectPtr 或数据表配置。

平台混音差异

同一个混音设置在 PC、主机和移动端听起来可能完全不同。主机通常有固定的扬声器配置,TV 扬声器的低频响应差;移动设备外放时动态范围很窄;PC 玩家可能用耳机,也可能用桌面音箱。

UE5.8 的 Platform Audio Settings 可以针对不同平台调整 Sound Class 的默认 Volume。我们项目的做法是:

  • PC 和主机使用同一套 Mix,但在主机版上把 BGM 整体降低 2 dB2\text{ dB},因为客厅环境里玩家离扬声器远;
  • 移动端额外加一个 MobileMix,把所有低频再压 3 dB-3\text{ dB},避免手机扬声器破音;
  • Switch 掌机模式单独处理,因为扬声器更小,语音的 Center Channel Volume 要提高。

平台差异不能靠耳朵在编辑器里一次调好。最好在每个目标设备上都跑一遍那个山谷爆炸场景,检查 BGM 是否退得足够、语音是否清晰、低频是否浑浊。

PC 耳机用户和主机 TV 用户的期望也不一样。耳机玩家对空间定位敏感,语音稍微偏一点都能听出来;TV 玩家在客厅环境里更依赖中置声道和整体响度。我们一般会在 PC 版把 Stereo Bleed 调低一点,让声音定位更准;主机版则适当提高 Voice Center Channel Volume,保证语音从电视正前方出来。

一些实战建议

做了几年项目音频之后,我总结出几条血泪教训:

  • 不要给每个 Sound Cue 单独调 Volume。统一挂到 Sound Class,再用 Mix 控制;
  • Sound Mix 的数量要克制。项目后期如果 Mix 超过二十个,调试成本会指数级上升;
  • Ducking 的 Fade Time 宁愿长一点,也别让玩家听到跳变;
  • 经常用 au.SoundMix.Debug 1 看当前激活了哪些 Mix,排查互相覆盖的 bug;
  • Snapshot 不要滥用,它适合环境切换,不适合高频事件;
  • 平台和耳机差异必须实机测试,编辑器里听到的和玩家听到的不是一回事。

写在最后

小林后来重新做了那个山谷场景。他没有再动每个 Sound Cue 的 Volume,而是把声音归到 Music、Ambient、SFX、Voice 四类,给爆炸做了一个 Duck Mix,给水下做了一个 Snapshot。演示那天,手雷炸响时背景音乐像被一只无形的手按了下去,队友的喊声从枪声里清晰地穿出来。

混音这件事,说到底就是让该响的响,该退的退。UE5.8 的 Sound Mix 和 Snapshot 给了你很细的工具链,但工具再多,耳朵才是最终裁判。

如果你刚开始做项目音频,别急着把每个参数都研究透。先把四类 Sound Class 搭好,做一个爆炸 Duck Mix,再做一个水下 Snapshot,跑一遍完整的场景。能听出区别了,再慢慢加语音优先和平台适配。音频系统和渲染系统不一样,它的反馈很直接,调好一听就知道。

小林后来也成了团队里的音频负责人。每次有新同事加入,他都会把那张山谷场景丢过去:先听一遍原始版本,再把 Mix 加上,听第二遍。两次对比,比任何文档都管用。