导语:做个实验:找一段你最怕的恐怖游戏实况,把音量关到零,再看一遍。Jump Scare 成了哑剧,走廊尽头那扇门就是一扇普通的门,手心的汗没了。把声音放回来,心率立刻回到一百二。混音师才是吓你的那个人——多边形只是他手里的工具。声音占了沉浸感预算的一半,分到的人力却往往不到项目一成——这一章,就是把这桩赔本买卖算清楚。
本章地图
mindmap
root((第14章 声音系统))
预算观
静音实验
三层声景
dB 与听感疲劳
资产层
MetaSounds 图
参数接口
Quartz 时钟
空间层
衰减与聚焦锥
空间化
遮挡与混响
结构层
自适应音乐
混音总线与 Ducking
实战
雨夜天台声景关掉声音,恐怖片就成了哑剧
导语里的实验建议每个人都做一遍,因为它能把"恐怖"拆成具体成分。静音之后,Jump Scare 剩下的只有画面跳变——大脑对跳变的反应是"愣一下",不是"心跳骤停"。真正让心率上 120 的,是跳变前 0.2 秒那个音频预告:一声不自然的寂静,或一个逆行的低频滑音。混音师在画面动手之前,就先掐住了你的喉咙。
再看一笔账。一个中型 3A 项目的常见配置:美术团队 60 到 100 人,音频团队 3 到 8 人;音乐与音效按分钟外包计价,摊进项目总预算经常不到一成。可玩家评测里那些"氛围""压迫感""紧张得不敢喘气",一半以上要靠耳朵兑现。声音是沉浸感里被克扣的那一半预算——不是因为它不重要,恰恰因为它太便宜,便宜到没人愿意为它早做打算。
程序视角下,这笔账还有另一层:音频管线和你已经懂的渲染管线几乎同构。资产(SoundWave)进逻辑图(MetaSounds),实例化成播放组件(UAudioComponent),过衰减、空间化、遮挡这套"音频版视锥剔除",最后进 Submix 与 Sound Class 做"音频版后处理"。学完本章,你手里有一套完整管线——不再是零散的 wav 播放调用。
flowchart LR
W[SoundWave 波形资产] --> G[MetaSoundSource 逻辑图]
G --> C[UAudioComponent 播放实例]
C --> A[SoundAttenuation 衰减/空间化/遮挡]
A --> S[Submix 路由与效果器]
S --> M[SoundClass / SoundMix 混音]
M --> D[Audio Device 混音输出]
D --> E((玩家耳朵))
K[SoundConcurrency 并发规则] -. 限制同时发声数 .-> C
O[Occlusion Trace] -. 挡则闷 .-> A三个最常见的误判,先排掉:
- "声音最后再放"——衰减半径、遮挡通道、并发上限全是关卡数据,最后放等于把关卡重摆一遍。
- "声音就是播 wav"——播一个 wav 只要一行 API;让一百个 wav 不打架、不穿帮、会呼吸,是系统工程。
- "响就行"——响度是最廉价的刺激,也是最快的疲劳源。下一节的 dB 账会算给你看。
还要认清音频债的偿还方式:它不在 code review 里暴露,在试玩会上暴露。玩家说"这段没感觉",查到最后,常常是雷声音量没错、但低通没跟上距离,或者音乐切换没卡拍。画面债用眼睛还,声音债用"感觉"还——而"感觉"最贵。
顺带说一句,程序员在音频侧其实是占便宜的一方:衰减半径是数值调参,自适应音乐是状态机,并发上限是资源预算,Ducking 是优先级仲裁。全是老本行,只是换了个单位。
补课·声景分三层,dB 是一把对数尺
补课:本节补音频基础——三层声景结构与 dB 尺度。音频出身的读者可直接跳到下一节。
声音设计的第一件事是把场景里的声音分类。行业惯用的分法正好三层,用程序员的概念翻译一遍:
- 拟音(Foley):与动作一一绑定的离散事件——脚步、开门、换弹、拾取。事件驱动,必须配变体池:同一个 wav 连放五次,玩家立刻出戏。
- 氛围(Ambience):持续运转的常驻服务——雨底、风声、机房嗡鸣、远处车流。循环播放,要求慢变化;一成不变超过 30 秒,它就成了"白色墙纸",被大脑过滤掉。
- 拟态(Score):情绪状态机——战斗音乐、探索铺底、剧情 stinger。它不负责"现场有什么",负责"现在该怕什么"。
对照渲染就好记了:拟音是前景物件,氛围是天光,音乐是后处理调色。三层各管一段,互不抢戏;混为一层的项目,后期必然打架。
变体池的规模给个下限:短促音效 4–6 个变体起步;脚步这种每秒都在响的,6–8 个变体加随机音高才撑得住一分钟。样本做不够就用参数凑——音高抖动、起始偏移、滤波扫动,都是廉价变体。
第二件事是尺子。分贝(dB)是对数刻度,不是线性的:声压每翻一倍 +6 dB,而人耳主观响度翻一倍大约要 +10 dB。游戏内说的是 dBFS(以满幅为 0 的相对电平),0 dBFS 就是天花板,混音的全部手艺就是在天花板底下分配空间。
N 个同样响的声源叠在一起,总响度远不是 N 倍:
10 个雨点源只比 1 个响 10 dB。这个公式直接决定声景的搭法:氛围层靠"种类"取胜,不靠"数量",堆同种声源是最亏的买卖。
还有一条曲线必须认识:等响曲线(Equal-Loudness Contour)。人耳对 2–5 kHz 最敏感——语音清晰度全在这一段——对低频天生迟钝,而且整体音量越小,低频感知掉得越快。推论很实际:玩家深夜把音量拧小,你精心调的"压迫感低频"第一个消失。混音时留一手:小音量下仍听得清的声音,优先级永远高于大音量下才爽的声音。
| 游戏内声音 | 相对电平参考 | 备注 |
|---|---|---|
| 耳语 / 呼吸 | −30 ~ −24 dBFS | 恐怖游戏的常客 |
| 雨声底 / 氛围床 | −28 ~ −18 dBFS | 比对话低一整档 |
| 脚步 | −20 ~ −12 dBFS | 玩家的"第二视角" |
| 对话 | −12 ~ −6 dBFS | 混音的锚点 |
| 枪声 / 爆炸 | −6 ~ 0 dBFS | 离天花板最近的位置 |
第三件事最容易被忽视:听感疲劳。恒定不变的声音会触发听觉适应——大脑的滤波器把它归档为"背景",30 秒后你就真的听不见机房嗡鸣了。持续高响度更糟,会造成暂时性阈移(TTS),等于把玩家的听力整体调暗一档。
推论很反直觉:一直响的警报等于没有警报。它先被适应机制过滤,再用掩蔽效应盖住真正重要的声音——脚步、换弹、队友语音。合格的警报设计只有三条:脉冲式而非长鸣;分级别,不同威胁不同音色;会自己闭嘴,响够次数自动降格为低频提示。这三条到混音总线那节还会回来。
掩蔽(Masking)是疲劳的帮凶:响的盖掉轻的,频段近的盖掉频段远的。警报和音乐铺底挤在同一频段,等于两个进程抢一把锁——谁响谁赢,不是谁优先级高谁赢,而玩家最后什么都没听清。设计任何一个声音之前先问一句:它活在哪个频段,邻居是谁。
最后警惕响度战争:所有东西都响,等于没有东西响——对比才是响度的意义。恐怖游戏最瘆人的瞬间往往是"突然安静",而安静只有在前面够热闹时才成立。
MetaSounds 接的是一份参数化的活
先回答"换掉的是什么"。Sound Cue 是 UE4 时代的音频图:随机容器、混合器、调制器三板斧,做"播放时随机挑一个 wav 再抖一下音高"这类事够用。它的天花板在于图是一次性的——触发完就跑完了。运行中想根据游戏状态连续改变声音,逻辑只能搬回蓝图:音频设计师看不懂,程序员嫌琐碎,两边都难受。
MetaSounds 把这件事换成了"可编程乐器"。它是一张样本级精度的 DSP 图:波形播放器、滤波器、包络、延迟线、随机源、数学运算,全在图内闭合运转。用第 8 章的语言讲:Sound Cue 之于 wav 相当于固定管线,MetaSounds 之于 wav 相当于材质系统之于贴图。
它值钱的地方有三个。
① 参数接口。MetaSoundSource 对外暴露输入参数,游戏侧通过 Audio Component 的 SetFloatParameter、SetBoolParameter、SetTriggerParameter 写入——战斗强度、积水深度、门窗开合,全是图内能直接消费的量。声音从"被触发"变成"被驱动"。
② Quartz 时钟。音乐切换要对齐小节边界才有"排练过"的感觉,否则就是硬切歌。Quartz 子系统提供样本级精度的音乐时钟,自适应音乐一节全靠它排队切换点;在 5.8 中对应官方文档的 Quartz 词条,细节以文档为准。
③ 图即数据。MetaSound 可以被当节点嵌进另一张图,能进版本控制、能 diff、能复用——对程序背景的团队,这是刚需。
代价也要说:MetaSounds 的图比 Sound Cue 深,而音频侧的剖析工具仍在追赶渲染侧(5.8 中先用 Unreal Insights 与 au.* 系列控制台命令,词条见延伸阅读)。旧工程的 Sound Cue 不必重写,两者同为 USoundBase,共存没有障碍;新资产直接上 MetaSounds。
图里能用的节点大致四类:音源(波形播放器、噪声源)、处理(滤波、延迟、包络、混合)、逻辑(MapRange、比较器、随机源)、触发(播放事件、定时器)。播放形态由接口约定:one-shot 走触发式接口,常驻循环走 Source 接口,对拍的音乐挂时钟接口——选接口就是选生命周期,5.8 的接口清单以官方 MetaSounds Reference 词条为准。
排查"参数没生效"按固定顺序来:组件是不是还持有这个声音的引用;参数名拼写对不对(TEXT 宏下没有编译期检查,写错静默失败);图的输入默认值是不是把写入顶掉了。这个顺序能省掉八成的玄学时间。
老工程迁移只换外壳不换内容:先把 Sound Cue 里的随机与混合逻辑搬进 MetaSounds 图,再逐个替换引用,按关卡批次走。别计划"一个周末全换完",那种计划的结果通常是两套系统并行到项目结束。
下面这段伪代码是一张典型的"强度驱动"图,它解决一个问题:同一个音乐循环,如何随战斗强度无级变"狠":
# 伪代码:MetaSounds 图「强度 → 滤波 + 空间感」
# 输入 Intensity ∈ [0,1],由战斗管理器用 SetFloatParameter 写入
inputs: Wave(loop), Intensity
cutoff = MapRange(Intensity, 0, 1, 400, 12000) # Hz:闷罐 → 开盖
wet = MapRange(Intensity, 0, 1, 0.0, 0.35) # 延迟发送量
lfo = Sine(frequency=0.1) # 缓慢呼吸,防死板
filter = StateVariableFilter(input=Wave, cutoff=cutoff + 200*lfo)
delay = DelayLine(input=filter, time=0.23, feedback=0.3)
out = Mix(dry=filter, wet=delay, alpha=wet)
OnPlay >> Wave.Start # 触发线:只进不出
out >> Output.Stereo # 数据线与触发线是两条河整张图没有一个分支,却实现了无级渐变——这是 MetaSounds 最典型的思维方式:别切,调。强度低时截止频率压在 400 Hz 附近,音乐发闷、有距离感;强度拉高,滤波"揭开盖子",高频全部放出来。要点注释:MapRange 是图的翻译官,所有游戏量都先过它;lfo 那 200 Hz 的抖动是为了让听感"活",静态参数是声景的死敌。
衰减、空间化与遮挡:声音也要过"视锥"
渲染管线靠视锥剔除决定画什么,音频管线靠衰减资产(Sound Attenuation)决定听见什么、听成什么样。一个衰减资产管四件事:距离、方向、遮挡、混响发送。
距离先说。三要素:衰减函数(Linear / Logarithmic / Inverse / NaturalSound)、内半径(InnerRadius,半径内不衰减)、衰减距离(FalloffDistance,到此归零)。选型背后是同一个物理:
点声源的能量摊在半径 r 的球面上,强度随 1/r² 下降;换算成声压的对数刻度,就是每拉开一倍距离 −6 dB。NaturalSound 按这个模型走,适合脚步、雷这类"该像真的"的声源;Logarithmic 远处衰减平缓,综艺感强,适合提示音;Inverse 在远处掉得太狠,大世界里一转身声源就"唰"地没了,慎用于长距离事件。
方向由空间化与聚焦锥分管。bSpatialize 打开后,立体声声像随声源方位实时平移;再选 Binaural(HRTF 双耳渲染),耳机玩家能听出"左后上方"这种精确方位——代价是 CPU 按发声路数计费,且对音箱外放收益有限。聚焦锥(Focus)给声源一个朝向:锥内满响度,锥外按系数衰减,喇叭广播、NPC 面朝喊话都靠它。
听者(Listener)挂在哪里是个独立决策。引擎默认把耳朵挂在相机上:第三人称游戏里,角色在左下角、耳朵在镜头后——所以你能听见自己角色背后的脚步。第一人称则通常挂在 Pawn 上。「回响峡谷」是第三人称,保持默认;真要改,入口在 PlayerController 的听者朝向设置,5.8 中对应官方文档的 Audio Listener 词条。
遮挡(Occlusion)是沉浸感的隐形功臣。引擎从声源向听者拉一条 trace,被几何挡住就同时做两件事:音量按 OcclusionVolumeAttenuation 压掉,再过一个低通滤波(截止频率由 OcclusionLowPassFilterFrequency 控制)把声音闷掉。物理直觉很简单:高频波长短,撞墙就散;低频波长长,绕墙而过——所以隔壁传来的声音永远是"闷的"。开销上每个 3D 声源一条 trace,几十路同时跑才需要开始心疼 CPU,真到那一步,还有按距离给遮挡检测降频的招。
空气吸收(Air Absorption)是衰减资产里另一个物理参数:距离越远,高频衰减越多。远处的雷只剩低频隆隆,就是它在起作用。衰减资产给高低频各设一个衰减距离,远雷的"闷"不用单独做资产,两个参数就有了。
还有一个运动才有的小现象:多普勒频移(Doppler Shift)。声源与听者沿连线相对运动时,频率被压缩或拉伸:
v_s 是声速(约 343 m/s),v_l、v_e 分别是听者与声源沿连线的速度分量。直觉版:物体冲你来,声音变尖;离你而去,声音变闷——警车呼啸而过的那声"咿——呜"就是它。引擎层在 5.8 中对应官方文档的 Doppler 词条;工程上更常见的做法是手动按相对速度缩放 PitchMultiplier,飞掠的子弹、擦肩的载具都这么干,可控性反而更高。
最后是混响,它能解释一个所有玩家都体验过、却说不清的现象:为什么走廊里的脚步声不一样。
耳朵判断空间大小,靠的是直达声占比。给一个简化模型:
D 越接近 1,声音越"干",大脑判定为小空间或露天;D 降下来,声音发"糊",大脑判定为大而硬的空腔。混响尾巴的长度用 RT60(衰减 60 dB 所需时间)估算:
V 是空间体积,A 是等效吸声面积。走廊麻烦在:体积不大,可四面硬墙让 A 更小,RT60 反而比同体积的房间长——每一步后面都拖着一条几百毫秒的回声尾巴。大脑听到这条尾巴,不用看画面就知道"这是条空旷的硬走廊"。这就是声学版的"环境叙事":混响参数本身就是场景描述。
引擎里的落地路径:子混音上挂 Reverb 效果器,各声源通过衰减资产的 Reverb Send 决定送多少湿声;空间切换用 Audio Gameplay Volume(5.8 中对应官方文档的 Audio Gameplay Volumes 词条),进走廊湿声比例拉高,回露天收干。雨夜天台一节会把它用满。
下面这段 C++ 是拟音层的标准动作:在脚底位置播放一步带完整空间行为的音效。
// 在脚底播放一步带衰减/并发/随机音高的音效(如"湿混凝土"脚步)
void AEchoCharacter::PlayFootstep(const FVector& FootLocation)
{
UWorld* World = GetWorld();
if (!World || !FootstepSound) { return; }
const float Pitch = FMath::RandRange(0.92f, 1.08f); // 防重复感
UGameplayStatics::PlaySoundAtLocation(
World, FootstepSound, FootLocation,
/*VolumeMultiplier*/ 0.9f, Pitch,
/*StartTime*/ 0.0f,
FootstepAttenuation, // USoundAttenuation*:半径+空间化+遮挡
FootstepConcurrency); // USoundConcurrency*:同帧最多 4 步
}要点:衰减与并发做成资产而不是写死在代码里,改数值不用重编译;随机音高 ±8% 是防"机关枪脚步"的最低配置。临时性的 PlaySoundAtLocation 只管"放",常驻层(雨底、音乐)要用 UAudioComponent 持着,才有参数可写——下一节就用上了。
classDiagram
USoundBase <|-- USoundWave
USoundBase <|-- UMetaSoundSource
USoundBase <|-- USoundCue : 旧系统·可共存
UAudioComponent --> USoundBase : 播放
UAudioComponent --> USoundAttenuation : 空间行为
UAudioComponent --> USoundConcurrency : 并发约束
USoundClass "1" o-- "*" USoundBase : 归类混音
USoundMix ..> USoundClass : 快照压音
UAudioComponent --> USoundSubmix : 路由
USoundAttenuation : InnerRadius / FalloffDistance
USoundAttenuation : bSpatialize / bEnableOcclusion
USoundAttenuation : ReverbSend
USoundConcurrency : MaxCount / ResolutionRule自适应音乐:横向换曲,纵向加层
自适应音乐只有两种基本手法,混用是新人最常犯的错。
纵向分层(Vertical Layering):同一首曲子的若干条分轨(stem)同时循环,按强度逐层开关。鼓组进、弦乐退,一拍之内见效,听感是"音乐变厚了"。
横向重编排(Horizontal Re-sequencing):整段换曲子,A 段切 B 段。必须等小节边界,否则就是夜店 DJ 切歌切在半拍上。听感是"音乐翻篇了"。
判断标准:情绪渐变用纵向,叙事跳变用横向。实战里八成需求是纵向——战斗强度本身就是个连续量,硬把它切成"换歌",每一段都是事故。
| 维度 | 纵向分层 | 横向重编排 |
|---|---|---|
| 切换时机 | 任意,一拍内 | 必须等小节边界 |
| 听感 | 变厚 / 变薄 | 翻篇 |
| 素材成本 | 一首曲 × N 条 stem | N 首完整曲 |
| 适用 | 战斗强度、潜行暴露度 | 过场、Boss 阶段转换 |
状态机用一个数驱动:威胁值 Threat。规则可以很简单——敌人感知到玩家 +1,发生交火 +2,连续 5 秒无战斗事件 −1。聚合交给一个战斗管理器(单例子系统即可,GameState 那本账的记法见第 3 章),音乐层只做一件事:把 Threat 映射成强度参数写进 Audio Component。
stateDiagram-v2
[*] --> 探索
探索 --> 遭遇: Threat ≥ 1(敌人感知)
遭遇 --> 激战: Threat ≥ 3 且持续 2s
遭遇 --> 探索: 丢失目标 10s
激战 --> 脱战: 敌全灭或拉开距离 5s
脱战 --> 探索: 尾奏小节播完
note right of 探索: 仅 Pad + 脉冲低音,强度层 1
note right of 遭遇: 加打击乐铺底,强度层 2
note right of 激战: 全编制 + 铜管断奏,强度层 3
note right of 脱战: 退层 + 预制尾奏,禁止骤停sequenceDiagram
participant AI as 敌人AI感知
participant CM as 战斗管理器
participant AC as 音乐AudioComponent
participant Q as Quartz时钟
AI->>CM: 感知到玩家 Threat+1
CM->>AC: SetFloatParameter Intensity=0.5
AC->>Q: 预约下一拍点
Q-->>AC: 拍点回调
AC->>AC: 淡入打击乐铺底(1小节)
AI->>CM: 交火 Threat+2
CM->>AC: SetTriggerParameter EnterCombat
AC->>Q: 预约最近小节边界
Q-->>AC: 边界回调
AC->>AC: 全编制切入 + 铜管断奏时序上有一条铁律:纵向分层随时能动,但隆重进入(Boss 出场、阶段转换)值得等一拍——卡在拍点上的铜管,比立刻响起的铜管更有压迫感。Quartz 时钟就是干这个的:音乐组件发请求,它在下一个拍点或小节边界回调。
脱战是翻车重灾区。敌全灭、音乐骤停,玩家脑子里蹦出来的第一个词是"出 bug 了",而不是"安全了"。正确顺序:激战层先退到遭遇层,播放一个预制的尾奏小节,再让 Pad 接管渐弱。状态机里"脱战"这个中间态,就是为这段尾巴存在的。
素材账也要算。一首三分钟的双层曲目 = 每段至少两条 stem;立体声 48 kHz / 16 bit 未压缩约 10 MB/分钟,全部常驻内存是灾难。长音频一律开 Streaming,压缩交给平台编码格式(5.8 中对应官方文档的 Audio Compression 词条)。到了「远港 Online」,这套状态机的 Threat 输入会从单人感知换成团队仇恨汇总——状态机不变,数据源换一根管子,这是后话。
层数别贪。三层强度(探索/遭遇/激战)加一层 stinger,是中小团队的甜蜜点;四层往上,混音师要对每一层组合单独调平衡,工作量按组合数爆炸。stinger 是横向手里的轻武器:单发、不属于任何循环,用在爆头、开箱、剧情揭示——它不用等小节,因为它本来就不在小节里。
给作曲的交付要求要提前说清楚:所有 stem 同 BPM、同小节数、同起始点,分轨命名带层号。少任何一条,纵向分层在引擎里就是对不齐的一盘散沙,返工的是双方。
混音总线:枪声、对话、音乐、环境抢一条车道
混音的本质是仲裁:天花板只有 0 dBFS 一个,四类声音抢同一条车道。先给四类逻辑总线排优先级:
- 对话(Dialogue):最高优先。叙事与战斗信息的载体,混音的锚点。
- 关键 SFX(枪声、命中、低血警报):玩法反馈,慢一拍就糊。
- 音乐:情绪层,可以被压,不能被砍。
- 环境(Ambience):存在感靠"缺了才发现",优先级最低。
UE 里的落地是三件套,别混:Sound Class 管"谁跟谁一族、谁压谁"(层级结构 + 被动混音修饰);Submix 管路由与效果器链(混响、EQ、压缩挂在总线上);Control Bus(Audio Modulation 插件)管运行时的参数化调节。三者各司其职,才能把闪避做成"设计师可配、程序可触发"的数据,而不是写在某个蓝图角落里的魔法数。
闪避(Ducking)机制一句话:对话开口,音乐自动 −8 dB、环境 −4 dB,说完再缓回来。两个时间参数决定它是"电影感"还是"抽水泵":压下要快(0.1–0.2 秒,跟着台词起),恢复要慢(0.5–1 秒,等尾音走完)。压快放快就是 pumping,玩家会以为音响接触不良。侧链压缩(Sidechain Compression)是同一件事的自动化版本:对话总线的电平实时驱动音乐总线的压缩器;5.8 内置 Dynamics Processor 的侧链配置字段以官方文档词条为准,配不好就退回快照方案,不丢人。
flowchart TD
GUN[枪声/命中 SFX] --> BX[Submix: SFX]
STEP[脚步/拟音事件] --> BX
DIA[对话] --> BD[Submix: Dialogue]
MUS[自适应音乐] --> BM[Submix: Music]
AMB[氛围床] --> BA[Submix: Ambience]
BX --> MAS[Master Submix]
BD --> MAS
BM --> MAS
BA --> MAS
BD -. Ducking -8dB .-> BM
BD -. Ducking -4dB .-> BA
BX -. 激战快照 -2dB .-> BM# 伪代码:对话触发的 Ducking 快照
# 载体三选一:SoundMix 快照 / Audio Modulation Patch / 侧链压缩
on dialogue_started(speaker):
push_snapshot("Duck_ForDialogue")
# 快照内容: Music 总线 −8 dB, Ambience −4 dB, SFX −2 dB
# 淡入 0.15 s —— 跟着台词起,不能迟到
on dialogue_finished(speaker):
schedule_pop("Duck_ForDialogue", delay=0.4) # 尾音留 0.4 s
# 淡出 0.6 s —— 恢复永远比压下慢,这是反抽吸的关键混音工作流只有一条建议:先把对话响度定死当锚,其他三类向它对齐。反过来做——先嗨音乐再压对话——是新手项目最常见的混音事故。等进入「远港 Online」,这张总线表还要加一路语音聊天(VOIP),优先级直接插到对话同级,闪避逻辑原样复用。
响度标准也补一句:整局混音最终要过平台响度规范(主机平台普遍以约 −23 LUFS 为参考线,具体数值以各平台认证文档为准)。但混音阶段先别看表,先把对话锚定——表是最后一道工序,不是第一道。另外给爆点留头顶空间(headroom):爆炸瞬间要还能再响 3 dB,这段余量是戏剧性的存款。
总线之外还有一层保险:SoundBase 的 Priority 字段决定发声路数被挤爆时谁被挤掉。低血警报拉满,环境音垫底,其余按玩法反馈分级——这个字段平时没人看,挤爆那一次就是它说了算。
「回响峡谷」雨夜天台:一张能呼吸的声景清单
回到示例线。这场戏的任务:主角雨夜追人穿过天台,途中有檐下躲雨段、积水段,远处雷暴在逼近,风从破窗往里灌。第 10 章用光讲了孤独与紧张,这一节用声音把同一句话再说一遍。
三层拆完,清单如下(衰减距离单位:虚幻单位,100 = 1 米):
| 声音 | 层 | 资产形态 | 衰减 | 空间化 | 并发 | 备注 |
|---|---|---|---|---|---|---|
| 雨声底 | 氛围 | 双声道循环,挂玩家组件 | 无(随听者) | 无,宽床 | 常驻 1 路 | 0.1 Hz LFO 让响度缓慢呼吸 |
| 远处雷 | 拟音 | one-shot,8–20 s 随机 | NaturalSound,内 2000 / 衰减 8000 | Binaural,方位天穹随机 | MaxCount 2,StopOldest | 空气吸收拉闷远雷 |
| 脚步(干/湿) | 拟音 | MetaSounds 双组样本 | NaturalSound,内 150 / 衰减 1200 | Binaural | MaxCount 4,StopFarthestThenPreventNew | 积水深度参数 crossfade;湿步 30% 概率叠溅水高频层 |
| 风穿缝 | 氛围(点源) | 循环 + 随机音高 | Logarithmic,内 80 / 衰减 900 | 空间化 + 窄聚焦锥对准窗缝 | 每缝 1 路,共 ≤3 | 走过窗缝那一下"扫耳"是报酬 |
| 檐口滴水 | 拟音 | one-shot,1–4 s 随机 | NaturalSound,内 50 / 衰减 600 | Binaural | MaxCount 6 | 檐下区域的"湿度证据" |
| 城市低频底 | 氛围 | 双声道循环 | 无 | 无 | 常驻 1 路 | −24 dBFS 上下;关掉才发现"世界死了" |
檐下与露天的切换不靠音量,靠两条通道:Audio Gameplay Volume 框出檐下区域,进入时把雨声层的低通压下来(雨变成"隔了一层")、Reverb 湿声从 10% 提到 30%;回露天反向。玩家听到的是"我躲进来了",而不是"雨小了"。
雷暴逼近交给一个全局 Intensity:天气状态机(数据层天气的做法见第 16 章)把逼近程度写成 0–1,MetaSounds 图里它同时控制远雷的触发间隔(20 s 收紧到 6 s)与低通截止(闷雷变炸雷)。一条参数,管三个听感维度。
并发预算顺带记一笔:这张清单全开约 15 路发声(雨底 1 + 雷 ≤2 + 脚步 ≤4 + 风 ≤3 + 滴水 ≤6 + 低频底 1),离引擎默认发声上限还很远。但把同一套思路搬进「远港 Online」的主城,50 个 NPC 的脚步加叫卖瞬间顶到天花板——并发资产里的 ResolutionRule 之所以存在,就是因为大场景需要一根保险丝,它从来不是装饰。
// 常驻雨声层:按"檐下/露天"实时改写 MetaSounds 参数
void URainLayerComponent::SetSheltered(bool bSheltered)
{
if (!RainComponent) { return; }
// 5.8 中参数写入对应官方文档词条:
// Audio Component → Set Bool / Float / Trigger Parameter
RainComponent->SetBoolParameter(TEXT("bSheltered"), bSheltered);
RainComponent->SetFloatParameter(TEXT("Intensity"),
bSheltered ? 0.35f : 1.0f);
}要点:常驻层必须用 UAudioComponent 持有,临时 one-shot 写不了参数;bSheltered 在图内驱动低通与混响发送,C++ 只管报状态,不管听感细节——听感归图管,状态归代码管,分工别乱。
最后做闭眼测试:闭眼只听,能不能说出空间多大、天气如何、檐口在哪、雷还有多远。听单有四条:一,雨底的 LFO 有没有被听出来——被听出来就是失败,呼吸感要停留在潜意识层;二,从露天走进檐下,像不像"推门进屋",而不是"有人拉了音量推子";三,连续两分钟,有没有哪个循环能被你数出拍子;四,闷雷与炸雷的距离感有没有拉开。哪条挂了,回去改清单,别动音量——声景的问题九成在结构,不在响度。
小结
声音是沉浸感里被克扣的一半预算:3 到 8 人的音频组扛玩家一半的氛围体验,越早进管线越省钱。声景三层各司其职:拟音是事件、氛围是常驻服务、音乐是状态机;混为一层,后期必然打架。dB 是对数尺:同种声源叠加收益递减,"一直响"等于"没有响"——警报要会闭嘴,氛围要会呼吸。MetaSounds 把声音从"被触发"变成"被驱动":参数接口加 Quartz 时钟,是自适应音乐的地基。混音先定对话为锚,Ducking 压快放慢;天花板只有一个,优先级表比音量推子重要。
上手任务
- 干湿脚步:给「回响峡谷」主角做干/湿两组脚步样本,积水深度参数 crossfade,随机音高 ±8%,每组不少于 6 个变体。验收:闭眼连走 30 秒,能明确分辨踩水与干地,且听不出样本重复。
- 两层自适应音乐:自定 Threat 聚合规则,实现探索/激战两层纵向切换。验收:Threat 拉满后 1 拍内鼓组进入;敌全灭后尾奏小节完整播完才回探索层,全程无骤停。
- 对话 Ducking:对白触发音乐 −8 dB、环境 −4 dB。验收:压下不超过 0.2 秒、恢复不短于 0.5 秒,录音波形上看不到台阶,连续 5 次对白无抽吸感。
下一章
声音把情绪讲完了,画面还差一个拿导筒的人。第 15 章 Sequencer 与镜头语言:把"播放动画"升级成"导演一场戏"。
延伸阅读
- 官方文档:MetaSounds Overview / MetaSounds Reference(图、参数接口与节点库)
- 官方文档:Sound Attenuation、Audio Gameplay Volumes、Audio Modulation、Quartz
- 源码:Engine/Source/Runtime/Engine/Classes/Sound/SoundAttenuation.h——衰减资产全部字段与注释
- 源码:Engine/Source/Runtime/Metasound——MetaSounds 运行时与节点实现
- GDC Vault:Naughty Dog 关于《最后生还者》声景与混音的分享(方向:叙事驱动混音);Hellblade 团队关于双耳录音(Binaural Audio)的分享(方向:耳机玩家的空间感)