Game audio is a
mixing graph plus a music system. Route every sound through a
small set of buses so you can balance and process groups; make music
react to
play through layering and re-sequencing rather than looping one track. This skill
teaches the portable practice; bind it to
, Unity's AudioMixer, or
middleware (FMOD/Wwise) for concrete APIs.
游戏音频是一个
混音图加音乐系统。将所有声音通过一组小型总线进行路由,以便你可以平衡和处理声音组;让音乐通过分层和重新排序做出响应,而非循环播放单一轨道。本技能教授可移植的实践方法;可将其与
、Unity的AudioMixer或中间件(FMOD/Wwise)绑定以使用具体API。
- Use to design a bus/mixer layout, set group volumes, and apply effects (reverb,
compression, EQ) to groups of sounds.
- Use to duck music/ambience under dialogue or impacts (sidechain).
- Use to build adaptive music that responds to combat/exploration intensity.
- Use to add SFX variation (pitch/sample randomization) and sync events to a beat.
When not to use: for the engine's concrete audio nodes/streams, use
or the engine's audio skill. Loading/streaming and asset import are
engine concerns. For UI sliders that drive bus volume, see the engine UI skill.
- 用于设计总线/混音器布局、设置组音量,以及为声音组应用效果(混响、压缩、均衡器)。
- 用于在对话或音效冲击下压低音乐/环境音(侧链闪避)。
- 用于构建响应战斗/探索强度的自适应音乐。
- 用于添加音效变体(音高/样本随机化)并将事件与节拍同步。
不适用场景: 若需使用引擎的具体音频节点/流,请使用
或引擎专属的音频技能。加载/流处理和资源导入属于引擎相关事项。若需实现驱动总线音量的UI滑块,请查看引擎UI技能。
- Lay out buses, not per-sound volume. A typical tree:
Master ← {Music, SFX, Ambience, UI, Voice}
. Everything plays into a bus; the player's settings
sliders map to bus volumes. Never set hundreds of clip volumes by hand.
- Work in decibels, not linear. Perceived loudness is logarithmic. Volume
controls and automation should operate in dB; convert only at the edges.
- Leave headroom. Mix so the Master peaks below 0 dBFS (aim for a target
loudness, e.g. around -14 to -16 LUFS for many games) to avoid clipping.
- Duck competing sources with a sidechain compressor (or volume automation):
when voice/important SFX plays, the music bus dips, then recovers.
- Make music adaptive via vertical layering (stems faded in/out) and/or
horizontal re-sequencing (swap segments at musical boundaries). See the
reference.
- Vary repeated SFX with small random pitch/volume offsets and sample pools
so footsteps and hits don't sound robotic.
- Verify on real output. Listen on headphones and speakers; check that the
mix balances, ducking is audible but not pumping, and music transitions land
on the beat — never assume from the editor meters alone.
- 规划总线布局,而非单独设置每个声音的音量。典型的树形结构:。所有声音都输入到总线中;玩家的设置滑块映射到总线音量。切勿手动设置数百个音频片段的音量。
- 使用分贝而非线性值工作。感知响度是对数的。音量控制和自动化应以分贝为单位操作;仅在边缘环节进行转换。
- 保留头部空间。混音时让主总线峰值低于0 dBFS(目标响度建议:许多游戏约为-14至-16 LUFS),以避免削波。
- 使用侧链压缩器(或音量自动化)压低竞争声源:当语音/重要音效播放时,音乐总线音量降低,随后恢复。
- 通过垂直分层(音轨淡入淡出)和/或水平重新排序(在音乐边界处切换片段)实现自适应音乐。请参考参考资料。
- 为重复音效添加变体:通过微小的随机音高/音量偏移和样本池,让脚步声和撞击声听起来不机械。
- 在真实输出设备上验证。通过耳机和扬声器聆听;检查混音是否平衡、闪避是否可闻但不突兀,以及音乐过渡是否贴合节拍——切勿仅依赖编辑器仪表板判断。
1. Bus routing and dB gain
1. 总线路由与分贝增益
Route sounds to named buses; control GROUPS, not individual clips.
将声音路由到命名总线;控制声音组,而非单个音频片段。
sfx_player.bus = "SFX"
music_player.bus = "Music"
sfx_player.bus = "SFX"
music_player.bus = "Music"
Map a 0..1 settings slider to decibels (linear_to_db), the perceptual unit.
将0..1范围的设置滑块值转换为分贝(linear_to_db),这是感知响度单位。
func set_bus_volume(bus_name: String, slider01: float) -> void:
var idx := AudioServer.get_bus_index(bus_name)
var db := linear_to_db(clamp(slider01, 0.0001, 1.0)) # 0 -> silence, 1 -> 0 dB
AudioServer.set_bus_volume_db(idx, db)
func set_bus_volume(bus_name: String, slider01: float) -> void:
var idx := AudioServer.get_bus_index(bus_name)
var db := linear_to_db(clamp(slider01, 0.0001, 1.0)) # 0 -> 静音,1 -> 0 dB
AudioServer.set_bus_volume_db(idx, db)
RIGHT: slider -> dB via linear_to_db. WRONG: assigning slider01 straight as dB
正确做法:滑块值通过linear_to_db转换为分贝。错误做法:直接将slider01赋值为分贝值
(a "0.5" would be only +0.5 dB — almost no change — and 0 would be 0 dB, full).
("0.5"仅代表+0.5 dB——几乎无变化——而0则代表0 dB,即最大音量)。
2. Ducking via sidechain (music dips under voice)
2. 通过侧链实现闪避(音乐在语音播放时压低)
A compressor on the MUSIC bus, keyed by the VOICE bus, lowers music while
音乐总线上的压缩器,以语音总线为触发源,在对话播放时降低音乐音量,随后恢复。这就是“侧链闪避”。
dialogue plays, then releases. This is "sidechain ducking".
设置(引擎特定):为音乐总线添加压缩器效果,并将其侧链设置为语音总线。然后调整参数:
Setup (engine-specific): add a compressor effect to the Music bus and set its
threshold(阈值):触发闪避的语音音量水平(例如-30 dB)
sidechain to the Voice bus. Then tune:
ratio(比率):闪避强度(例如8:1以实现明显压低)
threshold: level on Voice that triggers ducking (e.g. -30 dB)
attack(启动时间):快速(约10毫秒),以便音乐及时让开
ratio: how hard to duck (e.g. 8:1 for a clear dip)
release(释放时间):缓慢(约300-500毫秒),以便平滑恢复,避免突兀波动
attack: fast (~10 ms) so music gets out of the way promptly
无中间件替代方案:在语音开始时将音乐总线音量向下缓动,结束时恢复。
release: slow (~300-500 ms) so it recovers smoothly, not pumping
No-middleware alternative: tween the Music bus volume down on voice start and
func duck_music(active: bool) -> void:
var target_db := -12.0 if active else 0.0
create_tween().tween_method(
func(v): set_bus_volume_db("Music", v), current_music_db, target_db, 0.25)
func duck_music(active: bool) -> void:
var target_db := -12.0 if active else 0.0
create_tween().tween_method(
func(v): set_bus_volume_db("Music", v), current_music_db, target_db, 0.25)
3. SFX variation (kill the "machine gun" repeat)
3. 音效变体(消除“机关枪式”重复感)
Randomize pitch slightly and pick from a sample pool so repeats feel organic.
轻微随机化音高并从样本池中选择,使重复音效更自然。
func play_varied(samples: Array, bus := "SFX") -> void:
var p := AudioStreamPlayer.new()
p.stream = samples[randi() % samples.size()] # rotate through several takes
p.bus = bus
p.pitch_scale = randf_range(0.94, 1.06) # +/- ~6% pitch wobble
add_child(p); p.play()
p.finished.connect(p.queue_free) # clean up one-shots
func play_varied(samples: Array, bus := "SFX") -> void:
var p := AudioStreamPlayer.new()
p.stream = samples[randi() % samples.size()] # 循环使用多个采样版本
p.bus = bus
p.pitch_scale = randf_range(0.94, 1.06) # +/-约6%的音高波动
add_child(p); p.play()
p.finished.connect(p.queue_free) # 清理一次性播放的音频播放器
4. Beat-synced events (quantize to the music grid)
4. 节拍同步事件(对齐音乐网格)
Schedule gameplay/visuals on musical time, not frame time, so they land on beat.
按音乐时间而非帧时间安排游戏玩法/视觉效果,使其贴合节拍。
const BPM := 120.0
var seconds_per_beat := 60.0 / BPM
func current_beat(playback_position_sec: float) -> int:
return int(playback_position_sec / seconds_per_beat)
const BPM := 120.0
var seconds_per_beat := 60.0 / BPM
func current_beat(playback_position_sec: float) -> int:
return int(playback_position_sec / seconds_per_beat)
Quantize an action to the NEXT beat boundary instead of firing immediately.
将动作对齐到下一个节拍边界,而非立即触发。
func time_until_next_beat(pos: float) -> float:
return seconds_per_beat - fmod(pos, seconds_per_beat)
func time_until_next_beat(pos: float) -> float:
return seconds_per_beat - fmod(pos, seconds_per_beat)
Drive timing from the audio playback clock, which is steadier than frame delta.
从音频播放时钟获取时间,它比帧增量更稳定。
- Treating slider values as dB. Volume is logarithmic; map through
(and back with ). A linear slider on raw amplitude
feels like it does nothing until the very bottom.
- Per-clip volume instead of buses makes a global balance pass impossible and
bloats save/settings. Mix on buses.
- Clipping the master. Summed sounds exceed 0 dBFS and distort. Leave
headroom; put a limiter on Master as a safety net, not as the mixer.
- Pumping ducking: too-fast release or too-high ratio makes music audibly
breathe. Lengthen release; lower ratio.
- Looping a single music track for the whole game feels flat. Use layers or
segments that respond to state (see the reference).
- Beat sync off frame time. drifts; read the audio playback
position for musical timing, and account for output latency.
- Unbounded one-shot players: spawning AudioStreamPlayers without freeing
them leaks. Free on , or use a small pool.
- 将滑块值视为分贝。音量是对数的;需通过将范围的值转换为分贝(通过转换回来)。直接使用原始振幅的线性滑块会让人感觉几乎没有效果,直到滑块接近底部。
- 使用单片段音量而非总线会导致无法进行全局平衡调整,并使存档/设置臃肿。应通过总线进行混音。
- 主总线削波。叠加的声音超过0 dBFS会导致失真。保留头部空间;在主总线上设置限制器作为安全措施,而非混音工具。
- 闪避突兀波动:释放时间过快或比率过高会导致音乐明显起伏。延长释放时间;降低比率。
- 整个游戏循环播放单一音乐轨道会显得单调。使用响应状态的分层或片段(请参考参考资料)。
- 基于帧时间进行节拍同步。会产生漂移;应读取音频播放位置来进行音乐计时,并考虑输出延迟。
- 无限制创建一次性音频播放器:生成AudioStreamPlayer而不释放会导致内存泄漏。应在信号触发时释放,或使用小型对象池。
references/adaptive-music.md
— vertical layering vs horizontal re-sequencing,
transition timing (bars/quantize), stingers, intensity mapping, and crossfades.
references/adaptive-music.md
—— 垂直分层与水平重新排序、过渡时机(小节/对齐)、提示音、强度映射和交叉淡入淡出。
- — buses, , effects, and sync-to-beat in Godot.
- — trigger audio from input actions.
- — collision events that drive impact SFX.
- , — genres whose feel leans on audio feedback.
- —— Godot中的总线、、效果和节拍同步。
- —— 通过输入动作触发音频。
- —— 驱动冲击音效的碰撞事件。
- , —— 游戏体验依赖音频反馈的游戏类型。