Awake, Start e Update na Unity: a Ordem de Execução
Unity Start e Awake parecem fazer a mesma coisa, e é justamente essa confusão que gera o bug mais chato de iniciante: um script que funciona num dia e dá NullReferenceException no outro sem você ter mudado nada. A resposta curta: Awake roda primeiro e serve para o objeto preparar a si mesmo; Start roda depois que todos os Awake da cena terminaram e serve para conversar com outros objetos. Update roda a cada frame, depois disso tudo.
Aqui você vai ver a ordem acontecendo no Console, com dois objetos e um Debug.Log em cada método, e vai sair com uma regra de onde colocar cada linha de inicialização na Unity 6.3 LTS.
Ciclo de vida do MonoBehaviour em uma imagem
Todo script que herda de MonoBehaviour recebe chamadas da engine em momentos fixos. Você não chama Awake nem Update: a Unity chama, se o método existir na sua classe com o nome exato. A sequência que importa no dia a dia, para um objeto que já está na cena ao dar Play:
Awake: uma vez, quando a instância do script é carregada.OnEnable: logo depois, e de novo a cada vez que o objeto ou o componente é reativado.Start: uma vez, antes do primeiroUpdatedaquele script.- A cada frame:
FixedUpdate(zero, uma ou várias vezes, no passo da física),Update,LateUpdate, e depois a renderização. OnDisable: quando o objeto ou componente é desativado.OnDestroy: quando o objeto é destruído ou a cena é descarregada.
O manual da Unity tem o fluxograma completo na página "Order of execution for event functions", com física, animação, coroutines e renderização. Vale abrir uma vez para ter o mapa, mas os seis passos acima cobrem a maior parte das decisões de código.
Para ver isso funcionando, crie dois GameObjects vazios na cena, Jogador e Inimigo, e adicione o script abaixo nos dois. Arquivo LifecycleLogger.cs:
using UnityEngine;
public class LifecycleLogger : MonoBehaviour
{
private int updateCount;
private void Awake()
{
Debug.Log(Time.frameCount + " | " + name + " | Awake");
}
private void OnEnable()
{
Debug.Log(Time.frameCount + " | " + name + " | OnEnable");
}
private void Start()
{
Debug.Log(Time.frameCount + " | " + name + " | Start");
}
private void Update()
{
if (updateCount < 2)
{
Debug.Log(Time.frameCount + " | " + name + " | Update");
updateCount++;
}
}
private void OnDisable()
{
Debug.Log(Time.frameCount + " | " + name + " | OnDisable");
}
private void OnDestroy()
{
Debug.Log(Time.frameCount + " | " + name + " | OnDestroy");
}
}
Dê Play e olhe o Console. Você vai ver o Awake e o OnEnable do primeiro objeto, o Awake e o OnEnable do segundo, e só então os dois Start, seguidos dos Update. O detalhe que importa: nenhum Start roda antes de todos os Awake da cena. Isso está escrito na referência de MonoBehaviour.Start, e é a garantia que você vai usar para não depender de sorte.
O que a Unity não garante é qual dos dois objetos roda o Awake primeiro. O manual diz que, em geral, você não pode confiar na ordem em que o mesmo evento é chamado em GameObjects diferentes. Hoje pode ser Jogador antes de Inimigo; depois de reabrir a cena, pode inverter.
Awake: preparar o próprio objeto
Awake é o construtor do seu componente. Ele roda uma vez por instância e roda mesmo se o componente estiver desabilitado (com a caixinha do Inspector desmarcada), desde que o GameObject esteja ativo. Se o GameObject estiver inativo, o Awake espera até a primeira ativação.
Use Awake para tudo que só depende do próprio objeto:
- pegar componentes do mesmo GameObject (
GetComponent<Rigidbody2D>()); - inicializar variáveis a partir de valores do Inspector (
currentHealth = maxHealth); - registrar um singleton (
Instance = this).
Arquivo PlayerController.cs:
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public static PlayerController Instance { get; private set; }
[SerializeField] private int maxHealth = 5;
private Rigidbody2D body;
private int currentHealth;
public int CurrentHealth => currentHealth;
private void Awake()
{
Instance = this;
body = GetComponent<Rigidbody2D>();
currentHealth = maxHealth;
}
}
O que não vai no Awake: ler dados de outro objeto. Como a ordem entre objetos não é garantida, o outro objeto pode ainda não ter rodado o próprio Awake.
OnEnable e OnDisable
OnEnable roda logo depois do Awake e de novo toda vez que o objeto volta a ficar ativo. OnDisable é o espelho dele. Esse par é o lugar certo para coisas que precisam ligar e desligar junto com o objeto: inscrição em eventos, registro numa lista de inimigos ativos, habilitar uma InputAction.
Arquivo EnemyRegistry.cs, uma lista simples de inimigos ativos:
using System.Collections.Generic;
using UnityEngine;
public class EnemyRegistry : MonoBehaviour
{
private static readonly List<EnemyRegistry> active = new List<EnemyRegistry>();
public static int ActiveCount => active.Count;
private void OnEnable()
{
active.Add(this);
}
private void OnDisable()
{
active.Remove(this);
}
}
Se você usa Object Pooling, OnEnable vira o "reset" do objeto, porque o Awake não roda de novo quando ele sai da piscina. Vida cheia, timer zerado, cor normal: tudo isso vai no OnEnable de um objeto reaproveitado.
Start: falar com outros objetos
Start roda no frame em que o script está habilitado, antes do primeiro Update dele, e depois de todos os Awake da cena. Isso faz dele o lugar seguro para ler outro objeto, porque o outro objeto já se preparou.
Arquivo HealthBar.cs:
using UnityEngine;
public class HealthBar : MonoBehaviour
{
private PlayerController player;
private void Start()
{
player = PlayerController.Instance;
Debug.Log("Barra começa com " + player.CurrentHealth + " de vida.");
}
}
Se esse código estivesse no Awake de HealthBar, ele funcionaria quando o Awake do jogador rodasse antes e quebraria quando rodasse depois. No Start, PlayerController.Instance já existe e currentHealth já tem o valor certo, sempre.
Outro detalhe útil: Start pode ser uma coroutine. Se você declarar private IEnumerator Start(), pode usar yield return dentro dele para esperar um frame ou alguns segundos antes de continuar a inicialização.
Unity start vs awake: qual usar
A regra que eu uso em todo projeto cabe em duas linhas:
- Awake: eu comigo mesmo. Pegar meus componentes, inicializar meus campos, me registrar como singleton.
- Start: eu com os outros. Ler o jogador, pedir a referência do GameManager, consultar o estado de outro sistema.
Com essa separação, a ordem entre objetos deixa de importar. Todo mundo termina a própria preparação no Awake, e só depois alguém pergunta alguma coisa para alguém no Start.
Um caso que confunde: um objeto instanciado durante o jogo com Instantiate. O Awake e o OnEnable dele rodam dentro da chamada do Instantiate, antes da linha seguinte do seu código. O Start só roda depois, antes do primeiro Update do novo objeto. Então, se você instancia um inimigo e logo em seguida configura enemy.SetTarget(player), essa configuração acontece depois do Awake e antes do Start do inimigo. Isso é exatamente o que você quer: o inimigo se prepara, você configura, e no Start ele já usa o alvo certo.
Juntar ciclo de vida, física, input e UI num jogo inteiro é onde quem estuda sozinho costuma travar. Cada tutorial ensina uma peça isolada, e o bug aparece quando cinco peças disputam a mesma ordem de execução.
Update e o resto do loop
Depois do Start, cada frame passa pelos métodos de loop. Update roda uma vez por frame e é onde fica input, timers e lógica de jogo. FixedUpdate roda no passo fixo da física e LateUpdate roda depois de todos os Update, por isso é usado para câmera.
A diferença entre os três tem post próprio, com o bug do pulo perdido e o Fixed Timestep: Update vs FixedUpdate vs LateUpdate na Unity. Para o ciclo de vida, o que importa é que nenhum desses métodos roda antes do Start do mesmo script. Se o seu Update depende de algo que o Start prepara, está seguro.
E se o seu Update move algo, lembre de multiplicar por Time.deltaTime. O conceito por trás disso, independente de engine, está em delta time e movimento por frame.
Script Execution Order e quando não usar
Às vezes você precisa mesmo que um script rode antes de outro em todos os eventos. Um gerenciador de input que precisa ler o teclado antes de qualquer personagem, por exemplo. A Unity oferece dois jeitos:
- Project Settings > Script Execution Order: você adiciona scripts numa lista e dá um número para cada um. Menor roda antes. Scripts fora da lista ficam em "Default Time" (zero).
- Atributo
[DefaultExecutionOrder]na classe, que deixa a ordem no próprio código e vai junto se você copiar o script para outro projeto.
Arquivo InputReader.cs:
using UnityEngine;
using UnityEngine.InputSystem;
[DefaultExecutionOrder(-100)]
public class InputReader : MonoBehaviour
{
public static Vector2 Move { get; private set; }
private InputAction moveAction;
private void Awake()
{
moveAction = InputSystem.actions.FindAction("Move");
}
private void Update()
{
Move = moveAction != null ? moveAction.ReadValue<Vector2>() : Vector2.zero;
}
}
Com -100, o Update de InputReader roda antes do Update dos scripts sem ordem definida, então todo personagem lê o input do frame atual. O InputSystem.actions é o asset de ações do projeto, que vem configurado em projetos novos da Unity 6 com uma ação "Move" no mapa "Player".
Quando não usar: para consertar um bug de Awake lendo outro objeto. Se você começa a dar número de ordem para cada script, o projeto vira uma lista frágil que ninguém lembra por que existe. Antes de mexer na ordem, pergunte se o código está no método certo.
Erros comuns de ordem de execução
NullReferenceException no Awake ao ler outro objeto. O outro ainda não rodou o Awake. Mova a leitura para o Start.
Start nunca roda. O componente está desabilitado, ou o GameObject está inativo. Start só roda quando o script está habilitado. Confira a caixinha no Inspector e se o objeto pai está ativo.
Valor resetado ao reativar o objeto. Você colocou a inicialização no OnEnable sem querer, e ela roda a cada SetActive(true). Se era para acontecer uma vez, mova para o Awake.
Inimigo instanciado usa alvo vazio no Awake. Você configura o alvo depois do Instantiate, mas o Awake do inimigo já tentou usar o alvo. Leia o alvo no Start.
Método com nome errado. start() minúsculo, OnEnabled, Awake () com parâmetro. A Unity não acusa erro; o método só não é chamado. Se um log não aparece, confira a grafia antes de qualquer outra coisa.
Evento recebido por objeto destruído. Inscrição feita no Start sem desinscrição. Use o par OnEnable/OnDisable, que fecha a conta sozinho.
Próximo passo
Pegue o LifecycleLogger e faça três testes na cena: desmarque o componente num dos objetos e veja quais logs somem; desative o GameObject e ative pelo código depois de dois segundos; instancie um terceiro objeto pelo código e veja onde o Awake dele aparece no Console. Esses três testes fixam a regra melhor do que qualquer tabela.
Depois disso, siga para Update vs FixedUpdate vs LateUpdate, que é o próximo pedaço do loop. E quando o projeto tiver mais de uma dezena de scripts conversando, os eventos e o padrão Observer em padrões de projeto em jogos ajudam a manter a ordem de inicialização simples.
Perguntas frequentes
Qual a diferença entre Awake e Start na Unity?
Awake roda quando o script é carregado, mesmo que o componente esteja desabilitado, e serve para o objeto preparar a si mesmo. Start roda antes do primeiro Update do script, só se ele estiver habilitado, e depois que todos os Awake da cena já rodaram. Por isso Start é o lugar seguro para ler dados de outro objeto.
OnEnable roda antes ou depois de Start?
Antes. Num objeto ativo com o script habilitado, a sequência é Awake, OnEnable e só depois Start. A diferença é que OnEnable roda de novo toda vez que o objeto ou o componente é reativado, enquanto Awake e Start rodam uma vez só por instância.
Awake roda se o GameObject estiver desativado?
Não. Num GameObject inativo, Awake espera até a primeira vez que ele for ativado com SetActive(true). Já num GameObject ativo com o componente desabilitado, Awake roda normalmente; quem fica esperando é o Start.
Como garantir que um script rode antes de outro na Unity?
Use o atributo [DefaultExecutionOrder(numero)] na classe ou a tela Project Settings > Script Execution Order. Números menores rodam antes. Use com parcimônia: na maioria dos casos, separar o que vai em Awake e o que vai em Start resolve sem mexer em ordem nenhuma.



