Audio Manager no Godot 4: Música de Fundo e Efeitos com Autoload

Monte um audio manager no Godot 4 com autoload para tocar música de fundo em loop, efeitos sonoros sem cortar e volume por bus. Guia com GDScript tipado.
Todo jogo fica mais vivo com som, mas espalhar AudioStreamPlayer por todas as cenas vira uma bagunça rápido: a música corta ao trocar de fase, dois efeitos disparados juntos se cancelam e não dá para o jogador baixar só o volume da trilha. A solução é centralizar tudo num audio manager no Godot, um autoload único que qualquer script chama para tocar música de fundo e efeitos sonoros. Neste tutorial você vai montar esse gerenciador do zero, com GDScript tipado, pool de players e controle de volume por bus.
AudioStreamPlayer vs AudioStreamPlayer2D vs 3D
O Godot 4 tem três nós para tocar som, e escolher o certo evita dor de cabeça:
- AudioStreamPlayer: toca o áudio "direto no ouvido", sem posição no mundo. É o nó da música de fundo, dos sons de menu e de qualquer efeito que não precise vir de um lugar específico.
- AudioStreamPlayer2D: tem posição no mundo 2D. O som fica mais alto perto da câmera e mais baixo longe, com panning para os lados. Use para efeitos posicionais, como uma explosão num ponto do mapa.
- AudioStreamPlayer3D: o equivalente para jogos 3D, com atenuação por distância e som espacial.
Para o audio manager global, o AudioStreamPlayer puro é o que interessa: a música e a maioria dos efeitos de UI não têm posição. Efeitos posicionais você toca com nós 2D/3D dentro da própria cena do objeto que faz o som. Neste guia o foco é o gerenciador central de música e SFX genéricos.
Preparando os buses de áudio
Antes do código, organize o mixer. No editor, abra o painel Audio na barra inferior. Por padrão existe só o bus Master. Clique em Add Bus duas vezes e crie dois novos buses: Music e SFX. Deixe os dois com saída para o Master.
Essa separação é o que permite controlar volume de forma independente: a música vai toda para o bus Music, os efeitos para o bus SFX, e o Master controla o volume geral. Se quiser mais controle depois, como abaixar só a trilha durante um diálogo, vale a pena entender a fundo a mixagem com audio bus no Godot.
Com os buses prontos, o código vai simplesmente rotear cada player para o bus certo pelo nome.
Criando o autoload AudioManager
Crie um script audio_manager.gd. Um autoload não precisa estar preso a uma cena, então ele estende Node e monta os players por código no _ready().
extends Node
# Bus names devem bater com os buses criados no painel de Audio
const MUSIC_BUS: String = "Music"
const SFX_BUS: String = "SFX"
const SFX_POOL_SIZE: int = 8
var _music_player: AudioStreamPlayer
var _sfx_pool: Array[AudioStreamPlayer] = []
var _current_music: AudioStream = null
func _ready() -> void:
# Player dedicado para a musica de fundo
_music_player = AudioStreamPlayer.new()
_music_player.bus = MUSIC_BUS
add_child(_music_player)
# Pool de players para efeitos sonoros
for i: int in range(SFX_POOL_SIZE):
var player: AudioStreamPlayer = AudioStreamPlayer.new()
player.bus = SFX_BUS
add_child(player)
_sfx_pool.append(player)
Agora registre o script como autoload: Project > Project Settings > Globals > Autoload, aponte para res://audio_manager.gd, dê o nome AudioManager e clique em Add. A partir daqui, qualquer script no jogo chama AudioManager.play_music(...) ou AudioManager.play_sfx(...) sem precisar de referência nem get_node().
Função play_music() com loop e troca
A música de fundo tem um player só, porque você quer uma faixa por vez. A função play_music() recebe um AudioStream, evita reiniciar a mesma faixa que já está tocando e começa a tocar:
func play_music(stream: AudioStream, volume_db: float = 0.0) -> void:
if stream == null:
return
# Evita cortar e reiniciar a mesma faixa que ja esta tocando
if _current_music == stream and _music_player.playing:
return
_current_music = stream
_music_player.stream = stream
_music_player.volume_db = volume_db
_music_player.play()
func stop_music() -> void:
_music_player.stop()
_current_music = null
Para a faixa repetir sozinha, o loop é configurado no recurso de áudio, não no código. Em arquivos .ogg, selecione o arquivo no FileSystem, abra a aba Import e marque Loop, depois clique em Reimport. Em .wav, use o campo Loop Mode. Com o loop ativo, o AudioStreamPlayer repete a música indefinidamente.
Chamar fica simples, de qualquer cena:
@onready var _musica_fase: AudioStream = preload("res://audio/musica_fase1.ogg")
func _ready() -> void:
AudioManager.play_music(_musica_fase)
Trocando de faixa com fade
Cortar a música seca entre fases soa amador. Um fade curto suaviza a transição. Aqui usamos um Tween para baixar o volume, trocar a faixa e subir de novo. Como o autoload vive entre cenas, o fade nunca é interrompido por uma troca de cena:
var _music_tween: Tween = null
func fade_to_music(stream: AudioStream, duration: float = 1.0) -> void:
if _current_music == stream and _music_player.playing:
return
# Cancela um fade anterior que ainda esteja rodando
if _music_tween != null and _music_tween.is_valid():
_music_tween.kill()
_music_tween = create_tween()
# Baixa o volume atual ate o silencio
_music_tween.tween_property(_music_player, "volume_db", -40.0, duration * 0.5)
# Troca a faixa quando o som ja esta baixo
_music_tween.tween_callback(func() -> void: _swap_music(stream))
# Sobe de volta ao volume cheio
_music_tween.tween_property(_music_player, "volume_db", 0.0, duration * 0.5)
func _swap_music(stream: AudioStream) -> void:
_current_music = stream
_music_player.stream = stream
_music_player.play()
Agora AudioManager.fade_to_music(musica_boss) faz a transição suave da trilha da fase para a do chefe. O -40.0 dB é praticamente silêncio; ajuste a duração conforme o ritmo do seu jogo.
Pool de players para efeitos sonoros
Aqui está o problema clássico: se você usar um único player para todos os SFX, disparar dois tiros quase juntos faz o segundo cortar o primeiro. A solução é o pool que já criamos no _ready(). A função play_sfx() procura um player que não esteja tocando e usa ele:
func play_sfx(stream: AudioStream, volume_db: float = 0.0) -> void:
if stream == null:
return
var player: AudioStreamPlayer = _get_free_sfx_player()
if player == null:
return
player.stream = stream
player.volume_db = volume_db
player.play()
func _get_free_sfx_player() -> AudioStreamPlayer:
# Retorna o primeiro player do pool que esta livre
for player: AudioStreamPlayer in _sfx_pool:
if not player.playing:
return player
# Todos ocupados: nao toca (evita estourar o mixer)
return null
Com oito players no pool, até oito efeitos tocam ao mesmo tempo sem se cortar. Se todos estiverem ocupados, o novo som é ignorado, o que é seguro: melhor perder um estalo do que travar o áudio. Se o seu jogo dispara muitos sons juntos (um tiroteio intenso, por exemplo), aumente SFX_POOL_SIZE.
Uma variação útil é permitir escolher o bus, para tocar, por exemplo, sons de UI num bus separado:
func play_sfx_on_bus(stream: AudioStream, bus_name: String, volume_db: float = 0.0) -> void:
var player: AudioStreamPlayer = _get_free_sfx_player()
if player == null:
return
player.bus = bus_name
player.stream = stream
player.volume_db = volume_db
player.play()
Controlando volume por bus
O grande ganho de separar Master, Music e SFX aparece agora. Para o menu de opções, você regula cada bus de forma independente. O Godot trabalha em decibéis, não em porcentagem: 0 dB é volume cheio e valores negativos abaixam o som. Para transformar um slider de 0.0 a 1.0 em dB, use linear_to_db():
func set_music_volume(linear: float) -> void:
_set_bus_volume(MUSIC_BUS, linear)
func set_sfx_volume(linear: float) -> void:
_set_bus_volume(SFX_BUS, linear)
func set_master_volume(linear: float) -> void:
_set_bus_volume("Master", linear)
func _set_bus_volume(bus_name: String, linear: float) -> void:
var bus_index: int = AudioServer.get_bus_index(bus_name)
if bus_index < 0:
return
# linear 0.0 vira mudo, 1.0 vira 0 dB (volume cheio)
var volume_db: float = linear_to_db(clampf(linear, 0.0, 1.0))
AudioServer.set_bus_volume_db(bus_index, volume_db)
No menu de opções, conecte cada HSlider (com min_value = 0, max_value = 1, step = 0.01) à função certa:
func _on_music_slider_value_changed(value: float) -> void:
AudioManager.set_music_volume(value)
func _on_sfx_slider_value_changed(value: float) -> void:
AudioManager.set_sfx_volume(value)
Como o AudioManager é um autoload, esses ajustes valem para o jogo inteiro na hora. Vale integrar o menu de volume ao restante da interface quando você for montar como criar o menu principal no Godot, e salvar as preferências de áudio junto do sistema de save e load no Godot para que o jogador não perca a configuração ao fechar o jogo.
Testando o audio manager
Com tudo no lugar, um teste rápido confirma que o gerenciador funciona. Num script qualquer de cena:
@onready var _tiro: AudioStream = preload("res://audio/tiro.wav")
@onready var _musica: AudioStream = preload("res://audio/tema.ogg")
func _ready() -> void:
AudioManager.play_music(_musica)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_accept"):
AudioManager.play_sfx(_tiro)
Segure a tecla de confirmação e dispare vários tiros seguidos: graças ao pool, eles se sobrepõem sem cortar, enquanto a música segue tocando por baixo. Mude de cena e veja que a trilha continua, porque o autoload não morre na troca.
Boas práticas para o seu audio manager
Alguns cuidados deixam o sistema pronto para um jogo de verdade:
- Pré-carregue os sons mais usados com
preload()para evitar engasgos no primeiro disparo. - Não crie players sem controle. O pool de tamanho fixo protege o mixer de estourar quando muitos sons disparam juntos.
- Guarde os volumes num arquivo de save. Ler as preferências no
_ready()do autoload aplica a configuração do jogador logo na abertura. - Cheque os buses pelo nome com
get_bus_index()antes de mexer no volume; se retornar-1, o nome do bus está errado. - Use
AudioStreamPlayer2D/3Dpara sons posicionais dentro das cenas, deixando o AudioManager só para música e efeitos globais.
Com esse audio manager no Godot você tem uma base sólida: música em loop que atravessa fases, fade suave entre faixas, efeitos que nunca se cortam e volume ajustável por categoria. É pouco código, tipado e reaproveitável em qualquer projeto. A partir daqui, é só popular o pool com os sons do seu jogo e deixar a trilha rolar.
Perguntas frequentes
Qual a diferença entre AudioStreamPlayer e AudioStreamPlayer2D?
O AudioStreamPlayer toca som sem posição, ideal para música de fundo e UI. O AudioStreamPlayer2D tem posição no mundo 2D e aplica atenuação e panning conforme a distância da câmera, ideal para efeitos posicionais como um inimigo atirando.
Por que usar um autoload para o audio manager?
Um autoload (singleton) fica vivo entre trocas de cena, então a música não corta quando você muda de fase e qualquer script chama AudioManager.play_sfx() de qualquer lugar sem precisar de referência.
Como tocar vários efeitos sonoros ao mesmo tempo sem cortar?
Use um pool de AudioStreamPlayer. Ao tocar um SFX, procure um player livre no pool e use ele. Assim, dois disparos simultâneos usam players diferentes e nenhum corta o outro.
Como controlar o volume da música e dos efeitos separadamente?
Crie buses Music e SFX no painel de Audio, roteie cada player para o bus certo e ajuste o volume com AudioServer.set_bus_volume_db(). Assim o jogador regula música e efeitos de forma independente.
Como fazer a música tocar em loop?
Marque o loop no recurso de áudio. Em arquivos .ogg, ative o Loop no painel de importação. Em .wav, configure o Loop Mode. O AudioStreamPlayer respeita esse loop e repete a faixa sozinho.
Volume em decibéis (dB) é a mesma coisa que porcentagem?
Não. O Godot usa decibéis: 0 dB é volume cheio e valores negativos abaixam o som. Para converter um slider de 0 a 1 em dB use linear_to_db(). O valor -80 dB equivale a mudo.


