Audio Mixer na Unity: Volume de Música e Efeitos no Menu
O Unity Audio Mixer é o asset que deixa o jogador controlar música e efeitos separados: você cria grupos, expõe o volume de cada um para o script e liga um slider do menu que converte 0 a 1 em decibéis. No fim deste guia você tem um menu com dois sliders que soam naturais, salvam a escolha do jogador, não zeram o mixer de forma errada e ainda abafam a música quando o jogo pausa.
Todo o código foi escrito para o Unity 6.3 LTS e usa só a API de UnityEngine.Audio, sem pacote extra.
O que é o Audio Mixer da Unity
Sem mixer, cada AudioSource toca direto no AudioListener da câmera, e a única forma de mexer no volume geral é mudar o volume de cada fonte ou o AudioListener.volume, que afeta tudo junto. Isso não serve para um menu de opções, onde o jogador quer música baixa e efeitos altos.
O Audio Mixer é um asset que fica entre as fontes e o listener. Ele sempre tem um grupo Master, e você cria grupos filhos abaixo dele. Cada AudioSource aponta a saída (campo Output) para um grupo. Cada grupo tem volume próprio em decibéis, pode receber efeitos (filtro, reverb, compressor) e o Master soma tudo no final.
A faixa de volume de um grupo vai de -80 dB, que é silêncio, até +20 dB de ganho, segundo a documentação do Unity 6.3. O valor 0 dB significa "o som como foi importado". Guarde esse número: é a origem do bug mais comum de slider de volume.
Criando grupos Master, Música e Efeitos
- No Project, clique com o botão direito e escolha Create > Audio Mixer. Chame de
MainMixer. - Dê dois cliques no asset para abrir a janela Audio Mixer. Na coluna da esquerda aparece a seção Groups com o Master.
- Selecione o Master e clique no + da seção Groups. Crie
Musica. Selecione o Master de novo e crieEfeitos. Os dois precisam ser filhos do Master, não um filho do outro. - Em cada
AudioSourceda cena, arraste o grupo certo para o campo Output. A fonte da trilha vai emMusica; tiros, pulos e passos vão emEfeitos.
Se o jogo tem diálogo ou ambiente, crie os grupos agora, mesmo que o menu só tenha dois sliders. Reorganizar saídas depois, com 200 prefabs prontos, é trabalho chato. Uma dica que economiza tempo: deixe os prefabs de efeito com o Output já preenchido, assim todo inimigo novo nasce no grupo certo.
Expondo o parâmetro de volume
O script não enxerga o volume do grupo até você expor o parâmetro:
- Selecione o grupo
Musicana janela do mixer. - No Inspector, clique com o botão direito no nome Volume e escolha Expose 'Volume (of Musica)' to script. Aparece uma setinha ao lado do campo.
- No canto superior direito da janela do Audio Mixer, abra Exposed Parameters. O parâmetro vem com um nome genérico, como
MyExposedParam. - Clique com o botão direito nele, Rename, e troque para
MusicVolume. - Repita para
Efeitoscom o nomeSfxVolume.
O nome é sensível a maiúsculas. MusicVolume e musicVolume são parâmetros diferentes para o SetFloat, e ele não lança exceção quando erra: só retorna false. Por isso o código abaixo guarda os nomes em constantes e confere o retorno.
Slider para decibel com Mathf.Log10
Aqui mora o problema. O slider do Unity UI vai de 0 a 1, o mixer trabalha em decibéis. A conversão errada que aparece em muito tutorial é mapear linear: Mathf.Lerp(-80f, 0f, valor). O resultado é um slider em que 80% do curso soa quase igual e o som despenca no último pedaço. O ouvido percebe volume de forma logarítmica, então a conversão também precisa ser.
A fórmula certa é dB = 20 * log10(linear). Com o slider em 1, dá 0 dB (volume original). Em 0,5, dá cerca de -6 dB, que soa como "metade". Em 0,0001, dá exatamente -80 dB, o silêncio do mixer.
E aí está o bug do zero: Mathf.Log10(0f) retorna infinito negativo. Mandar isso para o mixer gera comportamento estranho, e em alguns casos o som some até reiniciar o jogo. A correção é nunca deixar o valor chegar a zero: configure o Min Value do slider em 0.0001 e, por garantia, faça um clamp no código.
Configuração dos sliders no Inspector: Min Value 0.0001, Max Value 1, Whole Numbers desmarcado.
Arquivo VolumeSettings.cs:
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;
public class VolumeSettings : MonoBehaviour
{
public const string MusicParam = "MusicVolume";
public const string SfxParam = "SfxVolume";
private const float MinLinear = 0.0001f;
private const float DefaultLinear = 0.75f;
[SerializeField] private AudioMixer mixer;
[SerializeField] private Slider musicSlider;
[SerializeField] private Slider sfxSlider;
private void Start()
{
float music = PlayerPrefs.GetFloat(MusicParam, DefaultLinear);
float sfx = PlayerPrefs.GetFloat(SfxParam, DefaultLinear);
musicSlider.SetValueWithoutNotify(music);
sfxSlider.SetValueWithoutNotify(sfx);
ApplyVolume(MusicParam, music);
ApplyVolume(SfxParam, sfx);
musicSlider.onValueChanged.AddListener(OnMusicChanged);
sfxSlider.onValueChanged.AddListener(OnSfxChanged);
}
private void OnDestroy()
{
musicSlider.onValueChanged.RemoveListener(OnMusicChanged);
sfxSlider.onValueChanged.RemoveListener(OnSfxChanged);
PlayerPrefs.Save();
}
private void OnMusicChanged(float value)
{
ApplyVolume(MusicParam, value);
PlayerPrefs.SetFloat(MusicParam, value);
}
private void OnSfxChanged(float value)
{
ApplyVolume(SfxParam, value);
PlayerPrefs.SetFloat(SfxParam, value);
}
private void ApplyVolume(string parameter, float linear)
{
float clamped = Mathf.Clamp(linear, MinLinear, 1f);
float decibels = Mathf.Log10(clamped) * 20f;
if (!mixer.SetFloat(parameter, decibels))
{
Debug.LogWarning($"Parâmetro '{parameter}' não encontrado no mixer. Confira o nome em Exposed Parameters.");
}
}
}
Por que cada decisão:
Start, nãoAwake. A documentação doAudioMixer.SetFloatno Unity 6.3 diz para não chamar o método emAwake,OnEnableou noRuntimeInitializeLoadType.AfterSceneLoad, porque o resultado pode ser imprevisível. Chame emStartou depois.SetValueWithoutNotify. Carregar o valor salvo sem disparar o evento evita gravar de novo o que você acabou de ler.- O máximo é 0 dB, não +20. Dá para deixar o slider ir acima de 1 e chegar ao ganho positivo, mas isso estoura áudio que já foi mixado no máximo. Deixe o reforço para quem mixa, não para o jogador.
Salvando e carregando o volume
O PlayerPrefs resolve bem para duas preferências de volume. O código grava a cada mudança do slider e força o PlayerPrefs.Save() quando o menu é destruído, que é o momento em que o jogador terminou de mexer.
Tem uma armadilha: se o volume só é aplicado no Start do menu de opções, o jogo começa com o mixer no padrão e só respeita a escolha do jogador quando ele abre o menu. A correção é aplicar os valores salvos também na primeira cena, num objeto que sempre existe.
Arquivo AudioBootstrap.cs:
using UnityEngine;
using UnityEngine.Audio;
public class AudioBootstrap : MonoBehaviour
{
[SerializeField] private AudioMixer mixer;
private void Start()
{
Apply(VolumeSettings.MusicParam);
Apply(VolumeSettings.SfxParam);
}
private void Apply(string parameter)
{
float linear = Mathf.Clamp(PlayerPrefs.GetFloat(parameter, 0.75f), 0.0001f, 1f);
mixer.SetFloat(parameter, Mathf.Log10(linear) * 20f);
}
}
Coloque o AudioBootstrap na cena inicial (tela de título ou splash). Se o seu jogo tem um gerenciador de áudio persistente com DontDestroyOnLoad, esse código pode morar nele. A estrutura de um gerenciador desses é a mesma em qualquer engine; o post sobre barramentos de áudio e mixagem na Godot mostra a ideia no outro lado, e os conceitos de grupo, envio e efeito valem igual.
Juntar mixer, menu, pausa, cenas e salvamento é o ponto em que muita gente que aprende sozinha trava: cada peça funciona isolada, e o jogo inteiro não fecha. No curso de Unity você monta esse esqueleto (menu de opções, áudio, pausa e troca de cena) dentro de um jogo completo, com revisão de quem já entregou jogo.
Snapshots: abafar som na pausa
Um snapshot é uma fotografia de todos os parâmetros do mixer. O padrão é ter um Snapshot inicial; você cria outros no + da seção Snapshots da janela. A troca entre eles é interpolada, então dá para fazer a música ficar abafada e mais baixa quando o jogo pausa, em vez de parar seco.
Aqui entra uma regra que derruba muita gente: depois que um parâmetro recebe SetFloat, os snapshots param de controlá-lo. A documentação do Unity 6.3 diz isso com todas as letras. Se o seu snapshot de pausa tenta baixar o mesmo Volume que o slider controla, nada acontece.
A saída é usar o snapshot para outra coisa:
- No grupo
Musica, clique em Add Effect no Inspector e escolha Lowpass. - Crie o snapshot
Pausado. Com ele selecionado, baixe o Cutoff freq do Lowpass para algo perto de 800 Hz. - Volte ao snapshot normal e deixe o Cutoff em 22000 Hz (som limpo).
- No asset do mixer, mude Update Mode para Unscaled Time. Sem isso, se a sua pausa usa
Time.timeScale = 0, a transição do snapshot também congela.
Arquivo PauseAudio.cs:
using UnityEngine;
using UnityEngine.Audio;
public class PauseAudio : MonoBehaviour
{
[SerializeField] private AudioMixerSnapshot normalSnapshot;
[SerializeField] private AudioMixerSnapshot pausedSnapshot;
[SerializeField] private float transitionSeconds = 0.25f;
public void SetPaused(bool paused)
{
AudioMixerSnapshot target = paused ? pausedSnapshot : normalSnapshot;
target.TransitionTo(transitionSeconds);
}
}
Chame SetPaused(true) do mesmo lugar que abre o menu de pausa. O volume do jogador continua valendo, porque ele está em outro parâmetro, e a música ganha aquele som de "atrás de uma parede" que todo mundo reconhece. Esse tipo de detalhe é parte do que separa áudio funcional de áudio que ajuda o jogo.
Erros comuns: slider não linear e -80 dB no zero
O slider some com o som só no final. Você está convertendo de forma linear. Troque por Mathf.Log10(valor) * 20f.
Arrastar o slider até o zero trava o áudio. O slider está chegando a 0 e o Log10 devolve infinito negativo. Min Value em 0.0001 e clamp no código.
SetFloat não faz nada. Três causas, nessa ordem: o nome não bate com Exposed Parameters (maiúsculas contam), o parâmetro nunca foi exposto, ou o método roda no Awake. O Debug.LogWarning do código acima pega o primeiro caso.
Os efeitos continuam altos com o slider no mínimo. Alguma AudioSource está com Output vazio e toca direto no listener, fora do mixer. Procure fontes sem Output na Hierarchy, principalmente as criadas por script com AddComponent<AudioSource>(), e atribua outputAudioMixerGroup no código.
O snapshot de pausa não muda o volume. Ele está tentando mexer num parâmetro que já recebeu SetFloat. Use outro parâmetro (o Lowpass acima) ou chame mixer.ClearFloat("MusicVolume") antes, sabendo que aí o slider perde o controle até o próximo SetFloat.
O volume volta ao padrão quando o jogo abre. Falta aplicar os valores salvos na cena inicial, como no AudioBootstrap.
Próximo passo
Monte o mixer com três grupos (Música, Efeitos e Interface), ligue os sliders e teste no build, não só no editor: o PlayerPrefs grava em lugares diferentes em cada plataforma, e é no build que você confirma que a preferência sobrevive a fechar e abrir o jogo.
Depois, vá para o lado do design: leia o guia de áudio design para jogos para decidir o que cada grupo deve soar e quando o mixer deve mudar de snapshot durante o jogo (combate, menu, cutscene). O código deste post já aguenta essas trocas; o que falta é decidir o momento de cada uma.
Perguntas frequentes
Qual a faixa de volume do Audio Mixer da Unity?
O volume de um grupo vai de -80 dB (silêncio) até +20 dB de ganho. O valor 0 dB é o volume original do som, sem corte nem reforço. Por isso o slider do menu não pode mandar 0 a 1 direto para o mixer: precisa converter para decibéis.
Por que o slider de volume da Unity não parece linear?
Porque o ouvido percebe volume em escala logarítmica. Se você mapeia o slider de forma linear para -80 a 0 dB, quase todo o curso do slider soa igual e o som some de repente no fim. Converter com Mathf.Log10(valor) * 20 deixa a resposta natural.
Como expor um parâmetro no Audio Mixer da Unity?
Selecione o grupo no Audio Mixer, clique com o botão direito no nome Volume no Inspector e escolha Expose Volume (of Música) to script. Depois, na lista Exposed Parameters do topo da janela do mixer, renomeie o parâmetro para um nome curto, como MusicVolume. É esse nome que vai no SetFloat.
Por que o snapshot parou de mudar o volume depois que usei SetFloat?
A documentação da Unity avisa que, depois de um SetFloat, aquele parâmetro deixa de ser controlado pelos snapshots. Use o snapshot para outros parâmetros (um filtro passa-baixa, por exemplo) ou chame ClearFloat para devolver o controle ao snapshot.



