强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Godot 3 GDScript 教程 / Godot 3 GDScript 教程(十二):音频系统

音频系统

音频是游戏沉浸感的关键组成部分。Godot 3 提供了一套完整的音频系统,涵盖 2D/3D 空间音频、音频总线混音、音效处理以及动态音乐管理。


音频播放节点

Godot 3 提供三种音频播放节点,分别适用于不同场景。

节点对比

节点适用场景空间化衰减
AudioStreamPlayerBGM、UI 音效、全局音效
AudioStreamPlayer2D2D 游戏中的位置音效
AudioStreamPlayer3D3D 游戏中的位置音效

AudioStreamPlayer(非空间音频)

# 播放背景音乐
$BGMPlayer.stream = preload("res://audio/bgm_main.ogg")
$BGMPlayer.bus = "Music"
$BGMPlayer.volume_db = -6.0
$BGMPlayer.play()

# 延迟播放(从 1.0 秒处开始)
$BGMPlayer.play(1.0)

# 停止
$BGMPlayer.stop()

AudioStreamPlayer2D(2D 空间音频)

# 2D 环境中的位置音效
$ExplosionSound.stream = preload("res://audio/explosion.wav")
$ExplosionSound.bus = "SFX"
$ExplosionSound.max_distance = 500.0  # 超过此距离无声音
$ExplosionSound.attenuation = 1.0     # 衰减系数
$ExplosionSound.play()

常用属性

属性说明
max_distance声音可听的最大距离(2D/3D)
attenuation距离衰减系数(越大衰减越快)
unit_db基础音量(dB)
pitch_scale音高变化(1.0 为原速)
playing是否正在播放

音频总线(Audio Bus)

音频总线用于分组管理和处理音频。

常用总线架构

Master(主输出)
├── Music(背景音乐)
├── SFX(音效)
├── Voice(语音)
└── Environment(环境音)
# 代码创建自定义音频总线
func _ready():
    AudioServer.add_bus()
    AudioServer.set_bus_name(1, "Music")
    AudioServer.set_bus_send(1, "Master")

    AudioServer.add_bus()
    AudioServer.set_bus_name(2, "SFX")
    AudioServer.set_bus_send(2, "Master")

💡 提示:将音频分配到不同总线后,可以独立控制每组的音量、静音和音效处理。


音效处理

Godot 的音频总线支持插入各种音效处理插件。

常用音效类型

效果类型说明常见用途
AudioEffectReverb混响洞穴、大厅空间感
AudioEffectDelay延迟/回声山谷回声
AudioEffectChorus合唱丰富音色
AudioEffectCompressor压缩器平衡音量动态范围
AudioEffectLowPassFilter低通滤波器水下效果、隔墙听声
# 代码添加音效
func _ready():
    var reverb = AudioEffectReverb.new()
    reverb.room_size = 0.6
    reverb.damping = 0.5
    reverb.wet = 0.3
    AudioServer.add_bus_effect(AudioServer.get_bus_index("SFX"), reverb)

# 动态开关效果(进入洞穴时开启混响)
func enter_cave():
    var bus_idx = AudioServer.get_bus_index("SFX")
    AudioServer.set_bus_effect_enabled(bus_idx, 0, true)

func exit_cave():
    var bus_idx = AudioServer.get_bus_index("SFX")
    AudioServer.set_bus_effect_enabled(bus_idx, 0, false)

⚠️ 注意:音效处理会增加 CPU 开销,移动端建议谨慎使用复杂效果链。混响效果尤其消耗资源。


音量控制

# 使用 dB(分贝)控制音量
# 0 dB = 原始音量,-6 dB ≈ 原始音量的一半

$BGMPlayer.volume_db = linear2db(0.5)  # 50% 音量

# 全局音量设置系统(建议设为 Autoload)
extends Node

func set_master_volume(linear: float):
    AudioServer.set_bus_volume_db(0, linear2db(linear))

func set_music_volume(linear: float):
    var bus_idx = AudioServer.get_bus_index("Music")
    AudioServer.set_bus_volume_db(bus_idx, linear2db(linear))

func toggle_mute(bus_name: String):
    var bus_idx = AudioServer.get_bus_index(bus_name)
    AudioServer.set_bus_mute(bus_idx, not AudioServer.is_bus_mute(bus_idx))

💡 提示:UI 滑块通常使用 0~1 的线性值,而 Godot 内部使用 dB。使用 linear2db()db2linear() 进行转换。


音频流导入设置

常见格式对比

格式压缩方式文件大小适用场景
WAV无压缩短音效
OGG有损压缩背景音乐、长音频
MP3有损压缩背景音乐
# 运行时设置音频流的循环
var stream = preload("res://audio/ambience_rain.ogg")
stream.loop = true
$AmbiencePlayer.stream = stream
$AmbiencePlayer.play()

⚠️ 注意:3D 空间音频的音源必须使用单声道音频,立体声无法正确进行空间化处理。


动态音乐系统

动态音乐系统可以根据游戏状态实时调整音乐,增强沉浸感。

分层音乐系统

extends Node

onready var layers = {
    "calm": $MusicCalm,
    "action": $MusicAction,
    "danger": $MusicDanger
}

var target_volumes := {}
var fade_speed := 1.0

func _ready():
    for name in layers:
        layers[name].bus = "Music"
        layers[name].play()
        target_volumes[name] = 0.0
    target_volumes["calm"] = 1.0

func _process(delta):
    for name in layers:
        var current = db2linear(layers[name].volume_db)
        var new_vol = lerp(current, target_volumes[name], delta * fade_speed)
        layers[name].volume_db = linear2db(new_vol)

func set_music_state(state: String):
    for name in target_volumes:
        target_volumes[name] = 0.0
    if state in target_volumes:
        target_volumes[state] = 1.0

# 使用:进入战斗时切换
func _on_EnemyDetected():
    $MusicManager.set_music_state("danger")

环境音效

# 环境音效管理器
extends Node

var active_ambience := {}

func play_ambience(name: String, stream: AudioStream, fade_time: float = 2.0):
    if name in active_ambience:
        return
    var player = AudioStreamPlayer.new()
    player.stream = stream
    player.bus = "Environment"
    player.volume_db = -80.0
    add_child(player)
    player.play()
    active_ambience[name] = player

    var tween = Tween.new()
    add_child(tween)
    tween.interpolate_property(player, "volume_db", -80.0, 0.0, fade_time)
    tween.start()

func stop_ambience(name: String, fade_time: float = 2.0):
    if not name in active_ambience:
        return
    var player = active_ambience[name]
    var tween = Tween.new()
    add_child(tween)
    tween.interpolate_property(player, "volume_db", player.volume_db, -80.0, fade_time)
    tween.start()
    yield(tween, "tween_all_completed")
    player.queue_free()
    active_ambience.erase(name)

音频与游戏事件联动

实现音效池

extends Node

export var pool_size: int = 10
var sfx_pool: Array = []

func _ready():
    for i in pool_size:
        var player = AudioStreamPlayer.new()
        player.bus = "SFX"
        add_child(player)
        sfx_pool.append(player)

func play_sfx(stream: AudioStream, volume: float = 0.0, pitch_range: Vector2 = Vector2(0.9, 1.1)):
    for player in sfx_pool:
        if not player.playing:
            player.stream = stream
            player.volume_db = volume
            player.pitch_scale = rand_range(pitch_range.x, pitch_range.y)
            player.play()
            return

# 使用示例
var hit_sound = preload("res://audio/hit.wav")
$SFXPool.play_sfx(hit_sound, 0.0, Vector2(0.8, 1.2))

💡 提示:随机化 pitch_scale 可以让同一音效每次播放时略有不同,避免重复感。这是专业游戏开发的常用技巧。


游戏开发场景

场景一:UI 按钮音效

extends Button

export var click_sound: AudioStream
export var hover_sound: AudioStream

func _ready():
    connect("pressed", self, "_on_pressed")
    connect("mouse_entered", self, "_on_mouse_entered")

func _on_pressed():
    var player = AudioStreamPlayer.new()
    player.stream = click_sound
    player.bus = "UI"
    add_child(player)
    player.play()
    yield(player, "finished")
    player.queue_free()

func _on_mouse_entered():
    $HoverPlayer.stream = hover_sound
    $HoverPlayer.pitch_scale = rand_range(0.95, 1.05)
    $HoverPlayer.play()

扩展阅读

💡 总结:合理的音频总线架构是良好音频系统的基础。建议至少分出 Music、SFX、Voice 三条总线。音效池和 Pitch 随机化是提升音效品质的两个简单有效技巧。