audio-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Audio design

音频设计

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
godot-audio
, Unity's AudioMixer, or middleware (FMOD/Wwise) for concrete APIs.
游戏音频是一个混音图加音乐系统。将所有声音通过一组小型总线进行路由,以便你可以平衡和处理声音组;让音乐通过分层和重新排序做出响应,而非循环播放单一轨道。本技能教授可移植的实践方法;可将其与
godot-audio
、Unity的AudioMixer或中间件(FMOD/Wwise)绑定以使用具体API。

When to use

使用场景

  • 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
godot-audio
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.
  • 用于设计总线/混音器布局、设置组音量,以及为声音组应用效果(混响、压缩、均衡器)。
  • 用于在对话或音效冲击下压低音乐/环境音(侧链闪避)。
  • 用于构建响应战斗/探索强度的自适应音乐。
  • 用于添加音效变体(音高/样本随机化)并将事件与节拍同步。
不适用场景: 若需使用引擎的具体音频节点/流,请使用
godot-audio
或引擎专属的音频技能。加载/流处理和资源导入属于引擎相关事项。若需实现驱动总线音量的UI滑块,请查看引擎UI技能。

Core workflow

核心工作流

  1. 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.
  2. Work in decibels, not linear. Perceived loudness is logarithmic. Volume controls and automation should operate in dB; convert only at the edges.
  3. 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.
  4. Duck competing sources with a sidechain compressor (or volume automation): when voice/important SFX plays, the music bus dips, then recovers.
  5. Make music adaptive via vertical layering (stems faded in/out) and/or horizontal re-sequencing (swap segments at musical boundaries). See the reference.
  6. Vary repeated SFX with small random pitch/volume offsets and sample pools so footsteps and hits don't sound robotic.
  7. 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.
  1. 规划总线布局,而非单独设置每个声音的音量。典型的树形结构:
    主总线 ← {音乐、音效、环境音、UI、语音}
    。所有声音都输入到总线中;玩家的设置滑块映射到总线音量。切勿手动设置数百个音频片段的音量。
  2. 使用分贝而非线性值工作。感知响度是对数的。音量控制和自动化应以分贝为单位操作;仅在边缘环节进行转换。
  3. 保留头部空间。混音时让主总线峰值低于0 dBFS(目标响度建议:许多游戏约为-14至-16 LUFS),以避免削波。
  4. 使用侧链压缩器(或音量自动化)压低竞争声源:当语音/重要音效播放时,音乐总线音量降低,随后恢复。
  5. 通过垂直分层(音轨淡入淡出)和/或水平重新排序(在音乐边界处切换片段)实现自适应音乐。请参考参考资料。
  6. 为重复音效添加变体:通过微小的随机音高/音量偏移和样本池,让脚步声和撞击声听起来不机械。
  7. 在真实输出设备上验证。通过耳机和扬声器聆听;检查混音是否平衡、闪避是否可闻但不突兀,以及音乐过渡是否贴合节拍——切勿仅依赖编辑器仪表板判断。

Patterns

模式示例

1. Bus routing and dB gain

1. 总线路由与分贝增益

gdscript
undefined
gdscript
undefined

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,即最大音量)。

undefined
undefined

2. Ducking via sidechain (music dips under voice)

2. 通过侧链实现闪避(音乐在语音播放时压低)

gdscript
undefined
gdscript
undefined

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

back up on voice end.

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)
undefined
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)
undefined

3. SFX variation (kill the "machine gun" repeat)

3. 音效变体(消除“机关枪式”重复感)

gdscript
undefined
gdscript
undefined

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
undefined
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) # 清理一次性播放的音频播放器
undefined

4. Beat-synced events (quantize to the music grid)

4. 节拍同步事件(对齐音乐网格)

gdscript
undefined
gdscript
undefined

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.

从音频播放时钟获取时间,它比帧增量更稳定。

undefined
undefined

Pitfalls

常见陷阱

  • Treating slider values as dB. Volume is logarithmic; map
    0..1
    through
    linear_to_db
    (and back with
    db_to_linear
    ). 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.
    delta
    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
    finished
    , or use a small pool.
  • 将滑块值视为分贝。音量是对数的;需通过
    linear_to_db
    0..1
    范围的值转换为分贝(通过
    db_to_linear
    转换回来)。直接使用原始振幅的线性滑块会让人感觉几乎没有效果,直到滑块接近底部。
  • 使用单片段音量而非总线会导致无法进行全局平衡调整,并使存档/设置臃肿。应通过总线进行混音。
  • 主总线削波。叠加的声音超过0 dBFS会导致失真。保留头部空间;在主总线上设置限制器作为安全措施,而非混音工具。
  • 闪避突兀波动:释放时间过快或比率过高会导致音乐明显起伏。延长释放时间;降低比率。
  • 整个游戏循环播放单一音乐轨道会显得单调。使用响应状态的分层或片段(请参考参考资料)。
  • 基于帧时间进行节拍同步
    delta
    会产生漂移;应读取音频播放位置来进行音乐计时,并考虑输出延迟。
  • 无限制创建一次性音频播放器:生成AudioStreamPlayer而不释放会导致内存泄漏。应在
    finished
    信号触发时释放,或使用小型对象池。

References

参考资料

  • references/adaptive-music.md
    — vertical layering vs horizontal re-sequencing, transition timing (bars/quantize), stingers, intensity mapping, and crossfades.
  • references/adaptive-music.md
    —— 垂直分层与水平重新排序、过渡时机(小节/对齐)、提示音、强度映射和交叉淡入淡出。

Related skills

相关技能

  • godot-audio
    — buses,
    AudioStreamPlayer
    , effects, and sync-to-beat in Godot.
  • input-systems
    — trigger audio from input actions.
  • physics-tuning
    — collision events that drive impact SFX.
  • platformer
    ,
    roguelike
    — genres whose feel leans on audio feedback.
  • godot-audio
    —— Godot中的总线、
    AudioStreamPlayer
    、效果和节拍同步。
  • input-systems
    —— 通过输入动作触发音频。
  • physics-tuning
    —— 驱动冲击音效的碰撞事件。
  • platformer
    ,
    roguelike
    —— 游戏体验依赖音频反馈的游戏类型。