Áudio 3D Posicional na Godot 4 com AudioStreamPlayer3D

Aprenda áudio 3D posicional na Godot 4 com AudioStreamPlayer3D: som que vem da fonte, volume que cai pela distância, pan pela direção e boas práticas.
Se você toca o som do inimigo num AudioStreamPlayer comum, ele soa igual esteja o bicho colado em você ou do outro lado do mapa. Falta a informação mais importante de um jogo 3D: de onde o som vem. É isso que o áudio 3D posicional na Godot 4 resolve. Com o nó certo, o passo do inimigo fica mais alto quando ele se aproxima, some pela esquerda quando ele passa correndo e desaparece quando fica longe. O jogador sente a posição sem olhar.
A ferramenta pra isso é o AudioStreamPlayer3D. Ele é um nó posicionado no espaço, e a engine calcula sozinha o volume e o balanço estéreo com base em onde ele está em relação aos ouvidos do jogador. Neste tutorial eu mostro por que usar esse nó em vez do comum, como posicioná-lo no objeto que emite o som, as propriedades que realmente importam, e três padrões práticos: som na posição de um inimigo, um player reaproveitável e tocar som num ponto qualquer do mundo. Tudo em GDScript tipado do Godot 4.x.
Por que usar AudioStreamPlayer3D em vez do comum
A Godot tem três nós de player de áudio, e trocar um pelo outro é a causa número um de "meu som 3D não funciona":
AudioStreamPlayer: sem posição. Toca igual nos dois canais. Perfeito pra música, narração e sons de interface.AudioStreamPlayer2D: posicional no plano 2D, pan e volume pela posição na tela.AudioStreamPlayer3D: posicional no espaço 3D. É o que você quer pra passos, tiros, máquinas, vozes de NPC e qualquer coisa que exista dentro do mundo.
A diferença prática do AudioStreamPlayer3D é que ele faz dois cálculos automáticos. O primeiro é a atenuação: o volume cai conforme a fonte se afasta do ouvinte. O segundo é o pan (balanço estéreo): o som pende pra esquerda ou pra direita conforme a direção da fonte. Você não escreve nada disso, só posiciona o nó e a engine faz o resto a cada frame.
O "ouvido" do jogador, por padrão, é a Camera3D ativa da cena. Ou seja, a posição e a rotação da câmera definem o que soa perto, longe, à esquerda ou à direita. Isso funciona direto em jogos em primeira pessoa. Em terceira pessoa, às vezes você quer que a escuta aconteça na posição do personagem, não da câmera lá atrás. Pra isso existe o nó AudioListener3D: adicione ele onde quiser ouvir e chame make_current().
# Faz este nó virar o ouvinte, ignorando a Camera3D
func _ready() -> void:
var listener: AudioListener3D = $AudioListener3D
listener.make_current()
Posicionando o nó: filho do objeto que emite o som
A regra de ouro do áudio 3D posicional na Godot 4 é simples: o AudioStreamPlayer3D tem que estar onde o som nasce. Como ele é um nó espacial (Node3D), a forma mais limpa é torná-lo filho do objeto que emite. Assim ele acompanha o movimento de graça, sem uma linha de código atualizando posição.
Uma árvore típica de inimigo fica assim:
Inimigo (CharacterBody3D)
├── MeshInstance3D
├── CollisionShape3D
└── FootstepPlayer (AudioStreamPlayer3D)
Com o FootstepPlayer como filho, quando o inimigo anda o player anda junto, e a atenuação e o pan se atualizam automaticamente. Tocar o passo é só chamar o método:
extends CharacterBody3D
@onready var footstep_player: AudioStreamPlayer3D = $FootstepPlayer
func play_footstep() -> void:
# is_playing() evita cortar um passo pra recomeçar em cima do outro
if not footstep_player.is_playing():
footstep_player.play()
Os métodos são os mesmos do player comum: play() toca, stop() para, is_playing() diz se está tocando. O que muda é tudo que acontece por baixo por causa da posição.
As propriedades que importam
Selecione o AudioStreamPlayer3D no editor e você vê muita coisa no Inspector. Estas são as que fazem diferença no dia a dia, e dá pra setar todas por código:
var player: AudioStreamPlayer3D = $FootstepPlayer
player.stream = preload("res://audio/footstep.ogg")
player.unit_size = 4.0 # distância em que o volume começa a cair
player.max_db = 3.0 # volume máximo em decibéis
player.max_distance = 25.0 # além disso, silêncio total (0 = sem limite)
player.attenuation_model = AudioStreamPlayer3D.ATTENUATION_INVERSE_DISTANCE
player.panning_strength = 1.0 # intensidade do pan pela direção
player.bus = "SFX" # roteia pro bus de efeitos
O unit_size é o que mais confunde. Ele não é o alcance máximo: é a distância dentro da qual o som ainda soa cheio antes de começar a atenuar. Escala pequena de cena pede unit_size pequeno, escala grande pede maior. Se seu som "some cedo demais", aumente ele antes de mexer em qualquer outra coisa.
O max_distance corta a fonte de vez a partir de certa distância. Manter isso num valor sensato (não 0, que é infinito) é ótimo pra performance: sons longe simplesmente param de ser processados.
O attenuation_model decide a curva de queda do volume:
ATTENUATION_INVERSE_DISTANCE: padrão, queda natural e equilibrada.ATTENUATION_INVERSE_SQUARE_DISTANCE: cai mais rápido, deixa o som bem local.ATTENUATION_LOGARITHMIC: queda mais suave, o som viaja mais longe.ATTENUATION_DISABLED: volume fixo com a distância, mas ainda aplica o pan de direção.
Cone direcional, filtro por distância e bus
Três propriedades a mais que valem conhecer quando você quer capricho.
O cone direcional faz o som ter frente, como um alto-falante ou a boca de um NPC. Ligue emission_angle_enabled e defina o ângulo:
player.emission_angle_enabled = true
player.emission_angle_degrees = 45.0 # metade da abertura do cone
Com isso, um som atrás da fonte chega mais abafado do que um som na frente dela. Bom pra falantes, alto-falantes de arena e máquinas com uma saída óbvia.
O attenuation_filter_cutoff_hz simula como sons distantes perdem os agudos (o mundo real abafa as frequências altas com a distância). Baixar esse valor deixa fontes longe soando mais "abafadas", o que reforça a sensação de distância.
Sobre roteamento: assim como no player comum, o AudioStreamPlayer3D tem a propriedade bus. Sempre mande seus efeitos pra um bus de SFX em vez de deixar tudo no Master, pra conseguir controlar volume, aplicar reverb e mutar por grupo. Se você ainda não tem essa estrutura montada, vale ler antes o guia de mixagem com audio bus, porque o áudio 3D encaixa direto nela.
Efeito Doppler
O efeito Doppler é aquela mudança de tom quando uma fonte passa por você em alta velocidade (a sirene da ambulância que fica aguda ao chegar e grave ao se afastar). A Godot faz isso automaticamente, mas vem desligado.
player.doppler_tracking = AudioStreamPlayer3D.DOPPLER_TRACKING_PHYSICS_STEP
Você tem DOPPLER_TRACKING_IDLE_STEP (atualiza no frame de renderização) e DOPPLER_TRACKING_PHYSICS_STEP (atualiza no passo de física, melhor pra objetos movidos pela física). Use com moderação: faz sentido em carros, projéteis e naves, e nada em passos ou cliques de menu.
Padrão 1: som na posição de um inimigo
O caso mais comum. O player mora dentro do inimigo e você dispara nos momentos certos. Note o area_mask, que decide de quais Area3D com efeito de áudio (reverb de sala, por exemplo) essa fonte é afetada.
extends CharacterBody3D
@onready var growl_player: AudioStreamPlayer3D = $GrowlPlayer
func _ready() -> void:
growl_player.stream = preload("res://audio/growl.ogg")
growl_player.unit_size = 6.0
growl_player.max_distance = 30.0
growl_player.bus = "SFX"
growl_player.area_mask = 1
func alert() -> void:
growl_player.play()
Como o player é filho do inimigo, se ele estiver à sua direita o rosnado sai à direita, e some conforme ele recua. Zero código de posição.
Padrão 2: um AudioStreamPlayer3D reaproveitável
Instanciar um nó novo a cada tiro é desperdício. Um padrão melhor é ter um player que você reposiciona e reusa. Dá pra generalizar isso num gerenciador de áudio (audio manager) que serve a cena inteira, mas o núcleo é este:
extends AudioStreamPlayer3D
# Move este player pra uma posição do mundo e toca um som
func play_at(world_position: Vector3, sound: AudioStream) -> void:
global_position = world_position
stream = sound
play()
Chamando de qualquer lugar:
var sfx: AudioStreamPlayer3D = $ReusableSfxPlayer
sfx.play_at(target.global_position, preload("res://audio/hit.ogg"))
Como um player só toca um som por vez, para sons que se sobrepõem (vários tiros juntos) mantenha um pequeno pool de players e revese entre eles. Bem mais barato que criar e destruir nós no meio da ação.
Padrão 3: tocar som num ponto do mundo
Às vezes o objeto que emite o som já morreu no mesmo frame (uma explosão que destrói o inimigo). Se o player for filho dele, o som some junto. A solução é instanciar um player temporário no ponto, tocar e destruir quando acabar.
func play_sound_3d(sound: AudioStream, world_position: Vector3) -> void:
var player: AudioStreamPlayer3D = AudioStreamPlayer3D.new()
player.stream = sound
player.unit_size = 5.0
player.max_distance = 30.0
player.bus = "SFX"
add_child(player)
player.global_position = world_position
player.play()
# Libera o nó assim que o som terminar
player.finished.connect(player.queue_free)
O sinal finished dispara quando o stream chega ao fim, e aí o queue_free limpa o nó. É o padrão certo pra sons "dispare e esqueça" de eventos pontuais.
Boas práticas de performance
Áudio 3D é barato até deixar de ser. Alguns cuidados que evitam dor de cabeça:
- Limite o número de sons simultâneos. Dezenas de
AudioStreamPlayer3Dtocando ao mesmo tempo custam CPU. Um pool com reuso resolve a maioria dos casos. - Defina
max_distancesensato. Fontes além do limite param de ser processadas. Deixar em0(infinito) processa som que o jogador nem ouve. - Ajuste
unit_sizeà escala da cena antes de sair mexendo em decibéis. Metade dos problemas de "som errado" é escala. - Use Doppler só onde importa (veículos, projéteis), não em tudo.
- Roteie pra um bus de SFX pra controlar o grupo inteiro de uma vez.
Com esses três padrões e as propriedades certas, você cobre praticamente todo som que existe dentro do mundo do seu jogo. O próximo passo natural é integrar isso com o resto do áudio (música, UI, sliders de volume) num sistema central, e amarrar tudo à jogabilidade. Se você ainda está montando a base da sua cena tridimensional, comece pelo seu primeiro jogo 3D na Godot e depois volte pra dar vida ao mundo com som posicional.
Perguntas frequentes
Qual a diferença entre AudioStreamPlayer e AudioStreamPlayer3D?
O AudioStreamPlayer comum toca o som sem posição, igual nos dois alto-falantes (bom pra música e UI). O AudioStreamPlayer3D existe no espaço 3D: o volume cai com a distância e o pan muda com a direção da fonte em relação ao ouvinte.
O que a propriedade unit_size controla?
A unit_size define a distância (em unidades do mundo) em que o som ainda soa em volume cheio antes de começar a atenuar. Valor maior faz o som viajar mais longe antes de cair. Ajuste conforme a escala da sua cena.
Como o Godot sabe onde estão os ouvidos do jogador?
Por padrão a Camera3D ativa funciona como ouvinte. Se você quer separar a posição da escuta da câmera (jogo em terceira pessoa, por exemplo), adicione um nó AudioListener3D e chame make_current() nele.
Como escolho o modelo de atenuação certo?
ATTENUATION_INVERSE_DISTANCE é o padrão equilibrado. ATTENUATION_INVERSE_SQUARE_DISTANCE cai mais rápido (som fica local). ATTENUATION_LOGARITHMIC é mais suave. ATTENUATION_DISABLED mantém o volume fixo mas ainda aplica o pan de direção.
Áudio 3D pesa na performance?
Muitos AudioStreamPlayer3D tocando ao mesmo tempo custam CPU. Limite o número de sons simultâneos, use max_distance sensato pra cortar fontes longe e reaproveite players em vez de instanciar um por evento.


