关于 audio-system
面向 Godot 4.3+ 游戏开发者的音频系统技能指南,提供 GDScript 和 C# 示例。内容包括总线、空间音频、音乐管理、音效池、设置保存和自适应音乐。
主要功能
- 01音频总线路由与效果
- 022D 和 3D 空间播放
- 03通过 Autoload 淡入淡出音乐
- 04池化音效播放器
- 05保存音量设置
- 06自适应音乐流模式
适用场景
- 01分别调节音乐、音效和 UI 音量
- 02播放定位于游戏世界的声音
- 03在场景切换时交叉淡化音乐
- 04限制频繁音效播放时的播放器创建
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill audio-system安装后在 Claude Code 中输入/audio-system确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/audio-settings.mdinteractive-music.mdmusic-manager.mdsfx-pooling.md详细介绍
Godot 4.3+ 中的音频系统
所有示例均适用于 Godot 4.3+,不使用已弃用的 API。先展示 GDScript,再展示 C#。
**相关技能:**使用 event-bus 解耦音频触发,使用 save-load 持久化音频设置,使用 resource-pattern 创建音频数据容器。
1. 核心概念
音频节点类型
| 节点 | 维度 | 用途 |
|---|---|---|
AudioStreamPlayer | 非定位音频 | 音乐、UI 音效、全局音效 |
AudioStreamPlayer2D | 2D 定位音频 | 脚步声、枪声、环境音效 |
AudioStreamPlayer3D | 3D 定位音频 | 与 2D 相同,但在 3D 空间中 |
音频总线架构
Godot 会将所有音频通过总线路由(类似混音台)。
Master (always exists)
├── Music → volume, effects for background music
├── SFX → volume, effects for sound effects
│ ├── Footsteps → sub-bus for fine-tuning
│ └── Weapons → sub-bus for fine-tuning
└── UI → volume for menu sounds
**设置:**底部面板 → Audio 标签页 → 添加总线、设置名称并路由输出。
每个 AudioStreamPlayer 都有一个 bus 属性——将其设为目标总线名称(例如 "SFX"、"Music")。
2. 基本音频播放
GDScript
extends Node2D
@onready var sfx_player: AudioStreamPlayer2D = $AudioStreamPlayer2D
@onready var music_player: AudioStreamPlayer = $MusicPlayer
func _ready() -> void:
# Play background music (looping is set on the AudioStream resource)
music_player.play()
func play_jump_sound() -> void:
sfx_player.stream = preload("res://audio/sfx/jump.wav")
sfx_player.play()
C#
using Godot;
public partial class AudioExample : Node2D
{
private AudioStreamPlayer2D _sfxPlayer;
private AudioStreamPlayer _musicPlayer;
public override void _Ready()
{
_sfxPlayer = GetNode<AudioStreamPlayer2D>("AudioStreamPlayer2D");
_musicPlayer = GetNode<AudioStreamPlayer>("MusicPlayer");
_musicPlayer.Play();
}
public void PlayJumpSound()
{
_sfxPlayer.Stream = GD.Load<AudioStream>("res://audio/sfx/jump.wav");
_sfxPlayer.Play();
}
}
循环播放音频
循环播放在 AudioStream 资源上配置,而不是在播放器节点上:
- **WAV:**Import 标签页 → Loop Mode → Forward(或 Ping-Pong)
- **OGG:**Import 标签页 → Loop → On,设置 Loop Offset
- **MP3:**Import 标签页 → Loop → On
音乐始终使用 OGG Vorbis(文件更小,音质良好)。短音效使用 WAV(PCM 或 QOA,解码开销低)。避免将 MP3 用于音效——视编码器而定,开头可能会有填充静音。
3. 音频总线管理
在代码中设置音量
Godot 使用**分贝(dB)**表示音量。滑块需要进行线性值到 dB 的转换。
GDScript
# Get bus index by name
var bus_index: int = AudioServer.get_bus_index("SFX")
# Set volume in dB directly
AudioServer.set_bus_volume_db(bus_index, -6.0) # -6 dB ≈ half amplitude (db_to_linear(-6.0) ≈ 0.5)
# Convert linear (0.0–1.0) to dB — use for UI sliders
func set_bus_volume_linear(bus_name: String, linear: float) -> void:
var index := AudioServer.get_bus_index(bus_name)
AudioServer.set_bus_volume_db(index, linear_to_db(linear))
# Mute / unmute a bus
AudioServer.set_bus_mute(bus_index, true)
# Read current volume as linear (for displaying on a slider)
func get_bus_volume_linear(bus_name: String) -> float:
var index := AudioServer.get_bus_index(bus_name)
return db_to_linear(AudioServer.get_bus_volume_db(index))
C#
int busIndex = AudioServer.GetBusIndex("SFX");
// Set volume in dB
AudioServer.SetBusVolumeDb(busIndex, -6.0f);
// Linear to dB conversion for UI sliders
public void SetBusVolumeLinear(string busName, float linear)
{
int index = AudioServer.GetBusIndex(busName);
AudioServer.SetBusVolumeDb(index, Mathf.LinearToDb(linear));
}
// Mute / unmute
AudioServer.SetBusMute(busIndex, true);
// Read current volume as linear
public float GetBusVolumeLinear(string busName)
{
int index = AudioServer.GetBusIndex(busName);
return Mathf.DbToLinear(AudioServer.GetBusVolumeDb(index));
}
音频总线效果
在 Audio 面板(底部停靠面板)中为总线添加效果。常见效果:
| 效果 | 用途 |
|---|---|
Reverb | 洞穴、大教堂、浴室的环境音效 |
Delay | 回声效果 |
Compressor | 标准化响亮或轻柔的声音(主总线) |
HardLimiter | 防止主总线发生削波(Limiter 已弃用) |
LowPassFilter | 模糊音效(水下、隔墙) |
HighPassFilter | 单薄、尖细的声音(收音机、电话) |
Chorus | 让声音更厚实 |
Distortion | 粗粝、过载效果 |
EQ | 微调频段 |
动态切换效果
# Enable/disable an effect on a bus at runtime
var bus_index := AudioServer.get_bus_index("SFX")
var effect_index := 0 # First effect on the bus
AudioServer.set_bus_effect_enabled(bus_index, effect_index, true)
# Apply low-pass filter for "underwater" feel
func set_underwater(enabled: bool) -> void:
var index := AudioServer.get_bus_index("SFX")
# Assumes a LowPassFilter is the first effect on the SFX bus
AudioServer.set_bus_effect_enabled(index, 0, enabled)
4. 空间音频(2D 和 3D)
AudioStreamPlayer2D
根据声音源与当前 AudioListener2D 的距离(如果没有当前监听器,则与屏幕中心的距离),自动调整音量和声像。
Enemy (CharacterBody2D)
├── Sprite2D
└── AudioStreamPlayer2D ← positioned at enemy's location
bus = "SFX"
max_distance = 1000.0
attenuation = 1.0
关键属性:
| 属性 | 描述 | 默认值 |
|---|---|---|
max_distance | 超过此距离后,声音将静音 | 2000.0 |
attenuation | 音量衰减曲线(1.0 = 线性,值越大衰减越陡) | 1.0 |
max_polyphony | 此播放器可同时播放的最大实例数 | 1 |
panning_strength | 声音左右声像移动的幅度 | 1.0 |
AudioStreamPlayer3D
概念相同,但用于 3D。可与 AudioListener3D(或 Camera3D)配合使用。
其他关键属性:
| 属性 | 描述 |
|---|---|
unit_size | 音量为 0 dB 时对应的距离 |
max_db | 最大音量上限 |
attenuation_model | Inverse、InverseSquare、Logarithmic、Disabled |
doppler_tracking | 为移动声源启用多普勒效应 |
AudioListener
# Make a specific camera the audio listener
# 2D: add AudioListener2D as child of Camera2D, call make_current()
# 3D: add AudioListener3D as child of Camera3D, call make_current()
# By default, the current Camera2D/3D acts as the listener.
# Only add an explicit AudioListener if you need a different listening position.
// 2D spatial player
public partial class Footsteps : AudioStreamPlayer2D
{
public override void _Ready()
{
Bus = "SFX";
MaxDistance = 1000.0f; // Pixels at which volume reaches zero
Attenuation = 1.0f; // Linear falloff (higher = sharper)
MaxPolyphony = 4; // Allow overlapping footstep sounds
}
public void PlayStep() => Play();
}
// 3D spatial player
public partial class EngineHum : AudioStreamPlayer3D
{
public override void _Ready()
{
Bus = "SFX";
UnitSize = 4.0f; // Meters at which volume is 0 dB
MaxDistance = 50.0f;
AttenuationModel = AttenuationModelEnum.InverseDistance;
}
}
// Custom listener — overrides the default Camera2D / Camera3D listener.
public partial class FollowCamListener : AudioListener3D
{
public override void _Ready() => MakeCurrent();
}
⚠️ Godot 4.7 中的变更:
AudioStreamPlayer2D/AudioStreamPlayer3D的默认area_mask从1改为0(禁用)——对于仍使用默认值的播放器,Area2D/Area3D上的audio_bus_override功能(例如水下总线)将停止工作。将area_mask设回第 1 层即可恢复;显式设为第 1 层以外值的遮罩不受影响。(迁移指南中提到的是“AudioStreamPlayer”,但area_mask仅存在于 2D/3D 变体中。)请参阅 4.7 迁移指南。
5. 音乐管理器(自动加载)
通过管理两个 AudioStreamPlayer 节点并对它们的 volume_db 进行补间的单例自动加载,在背景曲目之间交叉淡化。连接一个 Music 音频总线,让设置菜单可以单独调节音乐音量。
完整的 GDScript 和 C# 自动加载实现(交叉淡化、淡出停止、音乐总线音量)请参阅 references/music-manager.md。
6. 音效池
预先实例化一组固定数量的 AudioStreamPlayer 节点;play_sfx(stream) 会找到下一个空闲的播放器并播放。对于高频音效(枪声、脚步声、命中声),这种方式可以避免每次播放都实例化节点带来的开销。
GDScript 和 C# 的播放器池实现,以及 2D 定位音效变体(由跟随目标的
AudioStreamPlayer2D节点组成的池),请参阅 references/sfx-pooling.md。
7. 音频设置集成
将设置菜单中的 HSlider 连接到总线音量,通过 AudioServer.set_bus_volume_db(bus_idx, linear_to_db(value)) 进行设置。使用 ConfigFile 保存设置。请使用 linear_to_db / db_to_linear 辅助函数,切勿手动取对数。
包含持久化功能的完整设置菜单连接示例(GDScript + C#)请参阅 references/audio-settings.md。
8. 交互式与自适应音乐(Godot 4.3+)
自适应音乐有三种流类型:AudioStreamPlaylist(按顺序或随机播放曲目)、AudioStreamSynchronized(多个音轨同步播放——用于随战斗强度变化的垂直分层)、AudioStreamInteractive(由触发条件切换片段——由状态驱动的音乐)。Godot 4.4+ 添加了 AudioStreamWAV.load_from_file(),用于在运行时加载 WAV。
Godot 4.7+:
AudioStreamInteractive现在向脚本公开了TRANSITION_TO_TIME_PREVIOUS_POSITION(TransitionToTime枚举)——如果之前曾从目标片段切换过来,则目标片段会从上次播放的位置继续;否则从头播放。非常适合在探索音乐 ↔ 战斗音乐之间切换,并从中断处继续播放。
流类型对比、GDScript 示例、4.7+ 的续播位置切换,以及 4.4+ 的运行时加载示例,请参阅 references/interactive-music.md。
9. 音频导入最佳实践
| 格式 | 用途 | 文件大小 | 解码延迟 | 循环支持 |
|---|---|---|---|---|
| WAV | 短音效 | 大 | 无(PCM)/ 轻微(QOA,4.4+ 默认) | 通过导入 |
| OGG | 音乐、长音效 | 小 | 极低 | 通过导入 |
| MP3 | 音乐(备用) | 小 | 存在填充 | 通过导入 |
导入设置
在导入面板中(选择音频文件):
- Loop: 为音乐和环境循环启用
- BPM / Beat Count / Bar Beats: 为节奏同步游戏设置
- Force Mono(仅 WAV):为 3D 定位音频启用(立体声不适合空间化);在音频编辑器中将 OGG/MP3 导出为单声道
**提示:**音效使用 16 位、44.1kHz 的 WAV。将 WAV 导入设置中的 Compress > Mode 设为 PCM(未压缩),可实现零解码开销——4.4+ 默认使用 QOA(4.3 默认使用 PCM,标记为 Disabled)。音乐使用质量 6–8 的 OGG Vorbis,可获得良好平衡。
11. 常见问题
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 声音无法播放 | 播放器未添加到场景树中 | 确保在调用 play() 前通过 add_child() 添加 AudioStreamPlayer |
| 声音正在播放,但听不到音频 | 总线已静音或音量调低 | 在 Audio 面板中检查总线;拼写错误的 bus 名称会回退到 Master(绕过你的总线) |
| 切换场景后音乐重新开始 | 播放器属于场景,而不是自动加载 | 将音乐播放器移至自动加载项(MusicManager) |
| 3D 定位音频无声 | 没有当前的 Camera3D 或 AudioListener3D(2D 使用屏幕中心) | 添加 AudioListener3D,或确保 Camera3D 处于当前状态 |
| 声音出现咔哒声或爆音 | 音频文件没有淡入/淡出 | 在音频编辑器中为 WAV 的开头/结尾添加极短的淡入淡出(2–5ms) |
| 同时播放的声音过多 | 未限制复音数 | 在播放器上设置 max_polyphony,或使用 SFX 池 |
| 音量滑块感觉不是线性的 | 直接使用 dB,而不是进行线性转换 | 对滑块值使用 linear_to_db() / db_to_linear() |
| 3D 音频听起来像单声道/平淡 | 使用了立体声音源文件 | 3D 空间化请使用单声道文件(WAV:在 Import 标签页中选择 Force Mono) |
| MP3 开头有静音 | 文件中存在编码器填充 | 对时序要求严格的 SFX 使用 WAV,音乐使用 OGG |
12. 实现检查清单
- 已设置音频总线:Master、Music、SFX(至少这些)
- 所有 AudioStreamPlayer 节点都已指定正确的
bus属性 - 音乐使用 OGG Vorbis 格式;较短的 SFX 使用 WAV
- 音乐播放器位于自动加载项中(切换场景后仍可继续播放)
- 已实现音乐交叉淡化,以实现平滑过渡
- 使用 SFX 池,而不是动态创建新的 AudioStreamPlayer 节点
- 音量滑块使用
linear_to_db()/db_to_linear()转换 - 接近零的滑块值会将总线静音(避免
linear_to_db(0.0)=-inf) - 3D 音频源使用单声道音频文件,以实现正确的空间化
- 音频设置会在游戏启动时保存并恢复(使用 ConfigFile 或类似方式)
- 循环播放在 AudioStream 资源中配置,而不是在代码中配置
- 交互式/自适应音乐使用
AudioStreamPlaylist、AudioStreamSynchronized或AudioStreamInteractive(Godot 4.3+),而不是手动切换曲目的代码