NullReferenceException na Unity: Como Achar e Corrigir
Unity NullReferenceException ("Object reference not set to an instance of an object") quer dizer uma coisa só: o seu código tentou usar uma variável que vale null. Pode ser um campo que você esqueceu de arrastar no Inspector, um GetComponent que não achou o componente, um objeto que ainda não existia ou que já foi destruído. O Console diz o arquivo e a linha; a sua tarefa é descobrir qual variável daquela linha está vazia e por quê.
Este guia mostra como ler o erro e chegar na linha certa em segundos, passa pelas causas que mais aparecem em projeto de verdade (cada uma com o sintoma e a correção em C# para Unity 6.3 LTS), explica a diferença para UnassignedReferenceException e MissingReferenceException, e termina com hábitos que fazem esse erro quase sumir dos seus projetos.
Unity nullreferenceexception: o que significa
Em C#, variáveis de tipo referência (classes, componentes, GameObjects) guardam um endereço para um objeto. Quando não guardam nada, valem null. Declarar não cria o objeto:
private Rigidbody2D rb;
Essa linha cria uma variável vazia. Se o código chega em rb.linearVelocity = Vector2.zero; sem nunca ter posto um Rigidbody2D ali, a Unity lança NullReferenceException, porque não existe objeto para receber a velocidade.
O erro não é um bug da engine nem um problema de versão. É sempre uma pergunta sem resposta: "quem deveria ter preenchido essa variável, e quando?".
Ler o erro no Console e achar a linha
Uma mensagem típica:
NullReferenceException: Object reference not set to an instance of an object
PlayerHealth.TakeDamage (System.Int32 amount) (at Assets/Scripts/PlayerHealth.cs:23)
EnemyAttack.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/EnemyAttack.cs:15)
Leia de cima para baixo:
- Primeira linha: o tipo do erro.
- Segunda linha: onde ele aconteceu.
PlayerHealth.cs, linha 23, dentro deTakeDamage. - Linhas seguintes: quem chamou.
EnemyAttackchamouTakeDamagena linha 15.
Dê dois cliques na mensagem do Console e o editor de código abre na linha 23. Agora olhe todas as variáveis que aparecem antes de um ponto naquela linha. Em healthBar.fill.fillAmount = current / max;, os candidatos são healthBar e healthBar.fill. Um deles é null.
Para descobrir qual, ponha um log logo antes, usando o segundo parâmetro do Debug.Log:
Debug.Log("healthBar é null? " + (healthBar == null), this);
O segundo argumento é o contexto: ao clicar na mensagem no Console, a Unity destaca na Hierarchy o objeto que gerou o log. Isso resolve o caso clássico em que existem 30 inimigos e só um deles está com o campo vazio. Sem o contexto, você sabe que algum falhou; com ele, você sabe qual.
Causa 1: campo não arrastado no Inspector
A causa mais comum de todas. Você declara o campo, esquece de arrastar a referência e dá Play.
Arquivo PlayerHealth.cs:
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
[SerializeField] private Image healthFill;
[SerializeField] private int maxHealth = 100;
private int currentHealth;
private void Awake()
{
currentHealth = maxHealth;
}
public void TakeDamage(int amount)
{
currentHealth = Mathf.Max(currentHealth - amount, 0);
healthFill.fillAmount = (float)currentHealth / maxHealth;
}
}
Se healthFill ficou vazio no Inspector, a última linha falha. Aqui entra um detalhe da Unity: para campos serializados de tipos da Unity, você normalmente vê UnassignedReferenceException em vez de NullReferenceException, com uma mensagem bem mais útil, dizendo que a variável healthFill de PlayerHealth não foi atribuída e que você provavelmente precisa atribuí-la no Inspector. A correção é exatamente essa.
Atenção a dois casos que enganam:
- Instância na cena preenchida, prefab vazio. Você preencheu o campo na instância que estava na cena, não no asset do prefab, e depois instanciou por código. Abra o prefab (duplo clique no Project) e preencha lá.
- Dois componentes iguais no objeto. Um tem o campo preenchido e o outro não. O segundo gera o erro. Veja o Inspector inteiro.
Causa 2: GetComponent e Find retornando null
GetComponent<T>() devolve null quando o objeto não tem aquele componente. GameObject.Find("Nome") devolve null quando não existe objeto ativo com aquele nome, e o ponto "ativo" pega muita gente: Find não enxerga objeto desativado.
O jeito defensivo:
Arquivo EnemyAttack.cs:
using UnityEngine;
public class EnemyAttack : MonoBehaviour
{
[SerializeField] private int damage = 10;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.TryGetComponent(out PlayerHealth health))
{
health.TakeDamage(damage);
}
}
}
TryGetComponent devolve false quando o componente não existe, então o código nunca toca numa referência nula. É também mais barato que GetComponent seguido de checagem, porque não aloca nada no editor quando falha.
Para componentes que o script exige, deixe a Unity garantir:
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerMovement : MonoBehaviour
Com RequireComponent, adicionar o script adiciona o Rigidbody2D junto, e a Unity não deixa remover o componente enquanto o script estiver lá.
Sobre busca na cena: FindObjectOfType foi substituído na Unity 6 por FindFirstObjectByType e FindAnyObjectByType. Os dois devolvem null se não acharem nada. Use em inicialização, nunca dentro do Update, e sempre com checagem.
Até aqui você corrige um erro de cada vez. Num jogo com dezenas de scripts conversando, o que evita esses erros é arquitetura: quem cria quem, quem guarda referência a quem, quem avisa quem. Isso não aparece em tutorial de mecânica isolada, e é justamente o que o curso de Unity trabalha ao construir jogos completos, onde cada sistema precisa encontrar os outros sem quebrar.
Causa 3: ordem de execução
A Unity chama Awake de todos os objetos da cena antes de qualquer Start. Dentro de cada fase, a ordem entre scripts diferentes não é garantida. Um padrão que falha de forma intermitente:
GameManager.AwakefazInstance = this.PlayerHud.AwakelêGameManager.Instance.score.
Às vezes o HUD roda primeiro, Instance ainda é null, e o erro aparece. Às vezes não. A regra que resolve: no Awake, cada script prepara a si mesmo; no Start, conversa com os outros. Mova a leitura do HUD para o Start e o problema some.
Se realmente precisar de ordem, existe Edit > Project Settings > Script Execution Order, ou o atributo [DefaultExecutionOrder(-100)] na classe que deve rodar antes. Use pouco; ordem forçada vira dívida quando o projeto cresce.
Causa 4: objeto destruído
Um inimigo guarda referência ao alvo. O alvo é destruído com Destroy(gameObject). No frame seguinte, o inimigo tenta ler target.position e recebe MissingReferenceException: o objeto foi destruído, mas o script ainda tenta acessá-lo.
A Unity tem uma comparação especial: um objeto destruído é igual a null quando testado com ==. Então a checagem funciona:
if (target != null)
{
Vector3 direction = target.position - transform.position;
}
O que não funciona são os operadores ?. e ?? do C#. Eles olham a referência real, que ainda existe, e ignoram a comparação da Unity. target?.position num alvo destruído lança o erro do mesmo jeito. Com objetos da Unity (GameObject, componentes, ScriptableObject), use != null explícito.
Outra fonte frequente: evento assinado e não desassinado. O objeto é destruído na troca de cena, o evento estático continua chamando o método dele, e o método acessa o próprio transform. Assine no OnEnable e desassine no OnDisable, sempre em par.
Causa 5: prefab que precisa de objeto da cena
Você cria o prefab de um inimigo, e ele precisa saber onde está o jogador. Tenta arrastar o Player da Hierarchy no campo do prefab aberto, e a Unity não aceita. Não é bug: um asset de prefab existe fora de qualquer cena e não pode guardar referência a um objeto que só existe numa cena. Se o campo foi preenchido na instância que estava na cena, o inimigo funciona; os instanciados por código pelo spawner nascem com o campo vazio.
A correção é quem cria passar a referência. O spawner conhece o jogador (ele está na cena, então pode ter o campo arrastado) e entrega para cada inimigo logo depois do Instantiate. No spawner, o campo do prefab é tipado com o script do inimigo ([SerializeField] private EnemyChase enemyPrefab;), e o Instantiate devolve direto esse componente:
EnemyChase enemy = Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
enemy.Init(playerTransform);
Com um método Init(Transform target) público no inimigo, a dependência fica explícita: não existe inimigo sem alvo, e o erro, se acontecer, aparece no spawner, que é onde a referência faltou. É melhor que cada inimigo fazer FindFirstObjectByType sozinho, que esconde a dependência e custa caro com muitos inimigos.
UnassignedReferenceException e MissingReferenceException
As três exceções parecem iguais e apontam para causas diferentes:
| Exceção | Quando aparece | Onde olhar |
|---|---|---|
| NullReferenceException | Qualquer referência nula: variável nunca preenchida, GetComponent ou Find que não acharam | A linha do erro e quem deveria ter preenchido a variável |
| UnassignedReferenceException | Campo serializado que ficou vazio no Inspector | O Inspector do objeto citado na mensagem |
| MissingReferenceException | Objeto da Unity que já foi destruído | Quem chamou Destroy e quem ainda guarda a referência |
Na prática, quando a mensagem é Unassigned, a correção quase sempre é arrastar algo no Inspector. Quando é Missing, a correção é de lógica: checar antes de usar ou limpar a referência quando o objeto morre.
Como prevenir
Hábitos que fazem esse erro quase sumir:
[SerializeField] privatecom validação noAwake. Se um campo obrigatório está vazio, avise com contexto e desligue o script, em vez de deixar quebrar no meio do jogo:
private void Awake()
{
if (healthFill == null)
{
Debug.LogError("PlayerHealth sem healthFill em " + name, this);
enabled = false;
}
}
RequireComponentpara dependências no mesmo objeto.TryGetComponentpara dependências opcionais ou de colisão.- Inicializar no
Awake, conversar noStart. - Nada de
?.com objetos da Unity. - Ler o erro de cima para baixo antes de mudar código. Metade dos erros se resolve no Inspector.
Próximo passo
Na próxima vez que o Console ficar vermelho, faça o caminho completo: dois cliques, liste as variáveis da linha, log com contexto, ache a causa nesta lista. Se o erro for de objeto destruído, o post sobre MissingReferenceException na Unity aprofunda o caso. Para os tropeços que vão além deste erro, veja os erros mais comuns de iniciante em desenvolvimento de jogos, e se você também usa Godot, o guia de debug e erros comuns na Godot mostra o equivalente por lá.
Perguntas frequentes
O que significa "Object reference not set to an instance of an object" na Unity?
Significa que o código tentou usar uma variável que não aponta para nada, ou seja, que vale null. Acessar um campo, chamar um método ou ler uma propriedade de algo nulo lança NullReferenceException. A mensagem diz o arquivo e a linha onde isso aconteceu.
Qual a diferença entre NullReferenceException e UnassignedReferenceException?
UnassignedReferenceException é específica da Unity: aparece quando você usa um campo serializado que ficou vazio no Inspector, e a mensagem já diz qual campo e qual script. NullReferenceException é a exceção genérica do C# para qualquer referência nula, como um GetComponent que não achou nada.
O que é MissingReferenceException na Unity?
É o erro de usar um objeto que já foi destruído. A variável ainda guarda a referência, mas o objeto da Unity por trás dela não existe mais. A correção é checar com == null antes de usar ou deixar de guardar referência a objetos que podem ser destruídos.
Posso usar o operador ?. com objetos da Unity?
Não é recomendado. Os operadores ?. e ?? do C# ignoram a comparação especial da Unity, que faz um objeto destruído ser igual a null. Com eles, um objeto destruído passa pelo teste e o erro acontece do mesmo jeito. Use if (objeto != null) ou TryGetComponent.



