Godot do Zero: Áudio 2D e mixagem básica (SFX e música)

Capítulo 14

Tempo estimado de leitura: 9 minutos

+ Exercício

Visão geral: SFX 2D vs música

Em um jogo 2D, normalmente separamos o áudio em duas categorias:

  • SFX (efeitos sonoros): sons curtos e reativos (pulo, ataque, dano). Em geral, fazem sentido no espaço do jogo, então usamos AudioStreamPlayer2D para ter posicionamento (pan/atenuação) conforme a câmera.
  • Música: trilha contínua e não-posicional (geralmente). Usamos AudioStreamPlayer (sem 2D) para tocar em volume constante.

O objetivo aqui é: adicionar SFX de pulo/dano/ataque, configurar música, organizar tudo em buses (mixagem), criar controles de volume/mute na UI e persistir as configurações.

Preparando a mixagem: Audio Buses (Master, Music, SFX)

Criando buses no Audio Bus Layout

Abra o painel de áudio (Audio) e edite o Audio Bus Layout. Crie esta estrutura:

  • Master (já existe)
  • Music (saída para Master)
  • SFX (saída para Master)

Assim, você controla música e efeitos separadamente, sem mexer no Master o tempo todo.

Atribuindo bus aos players

Cada player tem a propriedade bus. Regras práticas:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Música: bus = "Music"
  • SFX: bus = "SFX"

Isso permite que sliders e mute atuem por categoria.

Adicionando música com AudioStreamPlayer (global)

Estrutura recomendada

Crie um nó dedicado para música (por exemplo, em uma cena global/gerenciador, ou na cena principal do jogo):

  • AudioStreamPlayer chamado MusicPlayer

Configure no Inspector:

  • stream: selecione o arquivo de música (OGG/WAV)
  • bus: Music
  • autoplay: opcional (você pode iniciar via código)
  • loop: se o stream suportar loop (ou configure no recurso)

Play/stop e troca de música

Exemplo de script simples para controlar a música:

extends Node

@onready var music_player: AudioStreamPlayer = $MusicPlayer

func play_music(stream: AudioStream, from_position := 0.0) -> void:
	if music_player.stream == stream and music_player.playing:
		return
	music_player.stream = stream
	music_player.play(from_position)

func stop_music() -> void:
	music_player.stop()

Boa prática: evitar reiniciar a mesma música se ela já estiver tocando.

SFX com AudioStreamPlayer2D: pulo, dano e ataque

Quando usar AudioStreamPlayer2D

Use AudioStreamPlayer2D quando o som deve “vir” do personagem/inimigo no mundo. Ele aplica:

  • Pan (esquerda/direita) conforme posição relativa à câmera
  • Atenuação com distância (se configurada)

Para SFX do jogador (pulo/ataque/dano), normalmente faz sentido ser 2D.

Passo a passo: SFX no Player

No nó do jogador, adicione três players 2D (ou um sistema centralizado; aqui vamos pelo caminho direto e didático):

  • AudioStreamPlayer2D chamado SfxJump
  • AudioStreamPlayer2D chamado SfxAttack
  • AudioStreamPlayer2D chamado SfxHurt

No Inspector de cada um:

  • stream: atribua o áudio correspondente
  • bus: SFX
  • volume_db: ajuste fino (ex.: -6 dB para não estourar)

No script do player, referencie e toque nos momentos corretos (exemplos de chamadas; adapte aos seus sinais/eventos):

@onready var sfx_jump: AudioStreamPlayer2D = $SfxJump
@onready var sfx_attack: AudioStreamPlayer2D = $SfxAttack
@onready var sfx_hurt: AudioStreamPlayer2D = $SfxHurt

func play_jump_sfx() -> void:
	_safe_play(sfx_jump)

func play_attack_sfx() -> void:
	_safe_play(sfx_attack)

func play_hurt_sfx() -> void:
	_safe_play(sfx_hurt)

Boas práticas essenciais para SFX

1) Evitar múltiplas instâncias simultâneas do mesmo som

Um erro comum é disparar o mesmo SFX várias vezes no mesmo frame (ou em sequência rápida), criando “embolado” e clipping. Para sons que não devem sobrepor (ex.: pulo), prefira reiniciar ou ignorar se já estiver tocando.

Crie uma função utilitária:

func _safe_play(player: AudioStreamPlayer) -> void:
	# Para sons curtos que não devem empilhar
	if player.playing:
		player.stop()
	player.play()

Se você quiser permitir sobreposição em alguns casos (ex.: tiros rápidos), uma alternativa é usar um pequeno “pool” de players (várias instâncias) e tocar no primeiro livre. Para este capítulo, manteremos o controle simples por som.

2) Pitch aleatório leve para variar repetição

Repetição idêntica de SFX (principalmente ataque) pode cansar. Uma variação pequena de pitch dá naturalidade sem distorcer demais.

func _safe_play_with_pitch(player: AudioStreamPlayer, min_pitch := 0.95, max_pitch := 1.05) -> void:
	player.pitch_scale = randf_range(min_pitch, max_pitch)
	if player.playing:
		player.stop()
	player.play()

Use com moderação (faixa estreita). Para dano, às vezes é melhor manter pitch fixo para consistência.

3) Pré-carregamento (preload) quando necessário

Se você carrega streams dinamicamente durante o jogo, pode haver micro travadas. Para SFX usados com frequência, prefira pré-carregar:

const SFX_JUMP: AudioStream = preload("res://audio/sfx/jump.ogg")
const SFX_ATTACK: AudioStream = preload("res://audio/sfx/attack.ogg")
const SFX_HURT: AudioStream = preload("res://audio/sfx/hurt.ogg")

func _ready() -> void:
	sfx_jump.stream = SFX_JUMP
	sfx_attack.stream = SFX_ATTACK
	sfx_hurt.stream = SFX_HURT

Para música, também é comum pré-carregar as faixas do menu/fase se forem poucas. Se forem muitas, carregue sob demanda, mas evite trocar em momentos críticos.

Controle de volume e mute via UI (sliders e botões)

Como volume funciona: dB vs linear

Na Godot, o volume do bus é em decibéis (dB). Sliders geralmente trabalham melhor em escala linear (0.0 a 1.0). Então precisamos converter:

  • linear_to_db(0.0) tende a -inf (silêncio)
  • linear_to_db(1.0) é 0 dB (volume original)

Vamos usar as funções embutidas linear_to_db e db_to_linear.

Passo a passo: UI de áudio no menu de opções

Na sua cena de opções, crie (nomes sugeridos):

  • HSlider MusicSlider (min 0, max 1, step 0.01)
  • HSlider SfxSlider (min 0, max 1, step 0.01)
  • CheckBox ou Button toggle MusicMute
  • CheckBox ou Button toggle SfxMute

Conecte os sinais:

  • value_changed(value) dos sliders
  • toggled(button_pressed) dos mutes

Aplicando volume/mute nos buses

Use AudioServer para controlar buses:

extends Control

@onready var music_slider: HSlider = $MusicSlider
@onready var sfx_slider: HSlider = $SfxSlider
@onready var music_mute: BaseButton = $MusicMute
@onready var sfx_mute: BaseButton = $SfxMute

var bus_music := AudioServer.get_bus_index("Music")
var bus_sfx := AudioServer.get_bus_index("SFX")

func _ready() -> void:
	# Inicializa UI a partir do estado atual dos buses
	music_slider.value = db_to_linear(AudioServer.get_bus_volume_db(bus_music))
	sfx_slider.value = db_to_linear(AudioServer.get_bus_volume_db(bus_sfx))
	music_mute.button_pressed = AudioServer.is_bus_mute(bus_music)
	sfx_mute.button_pressed = AudioServer.is_bus_mute(bus_sfx)

func _on_MusicSlider_value_changed(value: float) -> void:
	AudioServer.set_bus_volume_db(bus_music, linear_to_db(value))

func _on_SfxSlider_value_changed(value: float) -> void:
	AudioServer.set_bus_volume_db(bus_sfx, linear_to_db(value))

func _on_MusicMute_toggled(pressed: bool) -> void:
	AudioServer.set_bus_mute(bus_music, pressed)

func _on_SfxMute_toggled(pressed: bool) -> void:
	AudioServer.set_bus_mute(bus_sfx, pressed)

Dica: se você quiser que “mute” também mova o slider para 0 (ou vice-versa), faça isso explicitamente para não criar comportamento confuso.

Persistindo configurações de áudio (volumes e mute)

Estrutura de dados recomendada

Vamos salvar em user://settings.cfg usando ConfigFile. Campos típicos:

  • audio/music_volume (0..1)
  • audio/sfx_volume (0..1)
  • audio/music_mute (bool)
  • audio/sfx_mute (bool)

Script de Settings (autoload) para centralizar

Crie um script (ex.: Settings.gd) e adicione como Autoload (singleton) para ficar acessível em qualquer cena.

extends Node

const PATH := "user://settings.cfg"

var music_volume := 1.0
var sfx_volume := 1.0
var music_mute := false
var sfx_mute := false

func load_settings() -> void:
	var cfg := ConfigFile.new()
	var err := cfg.load(PATH)
	if err == OK:
		music_volume = float(cfg.get_value("audio", "music_volume", music_volume))
		sfx_volume = float(cfg.get_value("audio", "sfx_volume", sfx_volume))
		music_mute = bool(cfg.get_value("audio", "music_mute", music_mute))
		sfx_mute = bool(cfg.get_value("audio", "sfx_mute", sfx_mute))
	apply_audio()

func save_settings() -> void:
	var cfg := ConfigFile.new()
	cfg.set_value("audio", "music_volume", music_volume)
	cfg.set_value("audio", "sfx_volume", sfx_volume)
	cfg.set_value("audio", "music_mute", music_mute)
	cfg.set_value("audio", "sfx_mute", sfx_mute)
	cfg.save(PATH)

func apply_audio() -> void:
	var bus_music := AudioServer.get_bus_index("Music")
	var bus_sfx := AudioServer.get_bus_index("SFX")
	AudioServer.set_bus_volume_db(bus_music, linear_to_db(music_volume))
	AudioServer.set_bus_volume_db(bus_sfx, linear_to_db(sfx_volume))
	AudioServer.set_bus_mute(bus_music, music_mute)
	AudioServer.set_bus_mute(bus_sfx, sfx_mute)

Carregando no início do jogo

No _ready() do autoload (ou em um nó inicial do projeto), chame:

func _ready() -> void:
	load_settings()

Assim, o áudio já inicia com os volumes corretos antes de tocar música/SFX.

Integrando a UI com o Settings e salvando

No script da tela de opções, em vez de ler/escrever direto no AudioServer, atualize o singleton e aplique:

func _ready() -> void:
	# Garante que a UI reflita o que está salvo
	music_slider.value = Settings.music_volume
	sfx_slider.value = Settings.sfx_volume
	music_mute.button_pressed = Settings.music_mute
	sfx_mute.button_pressed = Settings.sfx_mute

func _on_MusicSlider_value_changed(value: float) -> void:
	Settings.music_volume = value
	Settings.apply_audio()
	Settings.save_settings()

func _on_SfxSlider_value_changed(value: float) -> void:
	Settings.sfx_volume = value
	Settings.apply_audio()
	Settings.save_settings()

func _on_MusicMute_toggled(pressed: bool) -> void:
	Settings.music_mute = pressed
	Settings.apply_audio()
	Settings.save_settings()

func _on_SfxMute_toggled(pressed: bool) -> void:
	Settings.sfx_mute = pressed
	Settings.apply_audio()
	Settings.save_settings()

Se você preferir não salvar a cada mudança (para reduzir escrita em disco), salve ao sair do menu (botão “Voltar/Aplicar”). A lógica é a mesma: atualizar variáveis, aplicar áudio e salvar uma vez.

Checklist rápido de mixagem e implementação

ItemO que verificar
BusesExistem Music e SFX roteados para Master
PlayersMúsica em AudioStreamPlayer (bus Music); SFX em AudioStreamPlayer2D (bus SFX)
VolumesUse slider 0..1 e converta com linear_to_db
MuteUse AudioServer.set_bus_mute por bus
RepetiçãoEvite empilhar o mesmo SFX: stop() antes de play() quando fizer sentido
VariedadeUse pitch_scale com variação leve em SFX repetitivos
PersistênciaSalvar/carregar com ConfigFile em user:// e aplicar no início

Agora responda o exercício sobre o conteúdo:

Qual abordagem melhor permite controlar separadamente o volume de música e de efeitos sonoros (SFX) no jogo, usando sliders na UI?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Separar em buses Music e SFX permite que sliders atuem por categoria. Como o bus usa dB, a UI trabalha em 0..1 e converte com linear_to_db ao aplicar no AudioServer.

Próximo capitúlo

Godot do Zero: Salvamento simples de progresso e configurações

Arrow Right Icon
Capa do Ebook gratuito Godot do Zero: Criando seu Primeiro Jogo 2D com GDScript
82%

Godot do Zero: Criando seu Primeiro Jogo 2D com GDScript

Novo curso

17 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.