Update vs FixedUpdate vs LateUpdate na Unity
Unity Update vs FixedUpdate, em uma frase: Update roda uma vez por frame de tela, FixedUpdate roda num ritmo fixo de física (50 vezes por segundo por padrão), e LateUpdate roda depois que todos os Update do frame terminaram. Daí sai a regra prática: input no Update, física no FixedUpdate, câmera no LateUpdate.
Este post mostra um exemplo de cada, com código C# do Unity 6.3 LTS, e explica o bug mais comum de quem mistura os três: o pulo que às vezes não sai.
Unity update vs fixedupdate: a diferença em uma frase
Pense em dois relógios. O primeiro bate a cada frame desenhado: se o jogo roda a 144 FPS, ele bate 144 vezes por segundo; se cai para 30, bate 30. É o relógio do Update. O segundo bate num intervalo fixo, 0,02 segundo por padrão, não importa o FPS. É o relógio do FixedUpdate, e é nele que a física da Unity (Rigidbody, colisões, forças) calcula o mundo.
A referência de MonoBehaviour.FixedUpdate diz que ele pode ser chamado zero, uma ou várias vezes por frame. Num frame que demorou 0,05 segundo, a engine roda duas ou três passadas de física para alcançar o tempo real. Num frame de 0,007 segundo (144 FPS), muitas vezes não roda nenhuma.
O LateUpdate usa o relógio do frame, como o Update, mas sempre depois dele. Dentro de um frame, a ordem é: zero ou mais FixedUpdate (seguidos da simulação da física), depois todos os Update, depois todos os LateUpdate, depois a renderização.
Update: input e lógica por frame
Update é o lugar padrão do código de jogo. Tudo que precisa reagir ao que o jogador fez neste frame vai aqui: ler botão, contar timer, trocar estado, disparar animação.
Arquivo DashCooldown.cs:
using UnityEngine;
using UnityEngine.InputSystem;
public class DashCooldown : MonoBehaviour
{
[SerializeField] private float cooldown = 1.5f;
private float timer;
private void Update()
{
if (timer > 0f)
{
timer -= Time.deltaTime;
}
Keyboard keyboard = Keyboard.current;
if (keyboard == null)
{
return;
}
if (keyboard.leftShiftKey.wasPressedThisFrame && timer <= 0f)
{
Debug.Log("Dash!");
timer = cooldown;
}
}
}
O wasPressedThisFrame é verdadeiro só no frame em que a tecla desceu. Esse "só neste frame" é o motivo de ele morar no Update: o Update roda em todo frame, então nenhum aperto escapa. Projetos novos da Unity 6 vêm com o Input System ativo, por isso o exemplo usa Keyboard.current em vez do antigo Input.GetKeyDown.
FixedUpdate: física em passo fixo
FixedUpdate é onde você mexe no Rigidbody2D ou Rigidbody: aplicar força, definir velocidade, fazer MovePosition. A razão é que a física simula logo depois do FixedUpdate (é o modo padrão de simulação), então o que você define ali entra na próxima passada de forma consistente, com o mesmo intervalo de tempo sempre.
Arquivo PlayerMover.cs:
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMover : MonoBehaviour
{
[SerializeField] private Rigidbody2D body;
[SerializeField] private float moveSpeed = 6f;
private InputAction moveAction;
private float horizontalInput;
private void Awake()
{
moveAction = InputSystem.actions.FindAction("Move");
}
private void Update()
{
horizontalInput = moveAction.ReadValue<Vector2>().x;
}
private void FixedUpdate()
{
body.linearVelocity = new Vector2(horizontalInput * moveSpeed, body.linearVelocity.y);
}
}
Repare na divisão: o Update lê o eixo, o FixedUpdate aplica. Ler um eixo contínuo dentro do FixedUpdate até funcionaria, porque o valor continua lá enquanto o direcional está pressionado, mas manter toda leitura no Update evita que você esqueça a regra quando o input for de um frame só. Na Unity 6, a propriedade é linearVelocity; o nome antigo velocity aparece em tutoriais velhos e está obsoleto.
Outro ponto: linearVelocity já está em unidades por segundo. Você não multiplica por Time.deltaTime aqui, a física faz essa conta.
LateUpdate: câmera e ajustes finais
Se a câmera segue o jogador no Update, você tem uma corrida: às vezes o Update da câmera roda antes do Update do jogador, a câmera vai para a posição antiga e o jogador anda depois. O resultado é um tremor sutil. No LateUpdate, o jogador já se moveu em todos os scripts, e a câmera pega a posição final.
Arquivo SimpleCameraFollow.cs:
using UnityEngine;
public class SimpleCameraFollow : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 offset = new Vector3(0f, 1f, -10f);
private void LateUpdate()
{
if (target == null)
{
return;
}
transform.position = target.position + offset;
}
}
Se o jogador se move por física, ainda falta uma peça: o Rigidbody2D só muda de posição nos passos de física, então num monitor de 144 Hz a posição dele fica parada por vários frames e depois pula. A câmera segue esses pulos fielmente e a imagem treme. A correção é marcar Interpolate no campo Interpolation do Rigidbody2D do jogador. Com isso, a Unity suaviza a posição visual entre um passo de física e outro, e a câmera no LateUpdate passa a seguir um movimento contínuo.
Em projeto de verdade, o Cinemachine resolve câmera com mais recursos, e ele também roda depois do movimento do alvo. O princípio é o mesmo.
O bug do input perdido no FixedUpdate
Este é o bug que motiva o post inteiro. O código abaixo, num arquivo BrokenJump.cs, parece certo e funciona quase sempre:
using UnityEngine;
using UnityEngine.InputSystem;
public class BrokenJump : MonoBehaviour
{
[SerializeField] private Rigidbody2D body;
[SerializeField] private float jumpSpeed = 12f;
private void FixedUpdate()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
body.linearVelocity = new Vector2(body.linearVelocity.x, jumpSpeed);
}
}
}
O problema: wasPressedThisFrame é verdade só durante o frame em que a tecla desceu. Se o jogo roda a 144 FPS, há frames sem nenhum FixedUpdate. Se o aperto cai num deles, o FixedUpdate seguinte já está no frame de depois, onde wasPressedThisFrame é falso. O pulo não sai. Em 50 FPS quase não acontece; em monitor rápido acontece o tempo todo, e o jogador jura que o controle está com defeito.
A correção é ler no Update, guardar a intenção e consumir no FixedUpdate. Arquivo PlayerJump.cs:
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerJump : MonoBehaviour
{
[SerializeField] private Rigidbody2D body;
[SerializeField] private float jumpSpeed = 12f;
private InputAction jumpAction;
private bool jumpRequested;
private void Awake()
{
jumpAction = InputSystem.actions.FindAction("Jump");
}
private void Update()
{
if (jumpAction.WasPressedThisFrame())
{
jumpRequested = true;
}
}
private void FixedUpdate()
{
if (jumpRequested)
{
body.linearVelocity = new Vector2(body.linearVelocity.x, jumpSpeed);
jumpRequested = false;
}
}
}
O jumpRequested sobrevive entre frames até um FixedUpdate consumir. Essa mesma ideia, guardar o aperto por um tempo, é a base do jump buffer, que deixa o pulo perdoar um aperto um pouco antes de tocar o chão. O passo a passo completo, com coyote time, está em como programar um pulo com coyote time e jump buffer.
Um jogo de plataforma de verdade junta esse pulo com animação, câmera, colisão de parede e checagem de chão. Cada peça isolada é simples; fazer todas respeitarem os mesmos três relógios sem uma quebrar a outra é a parte que costuma travar quem aprende sozinho.
Fixed Timestep e Time.fixedDeltaTime
O intervalo do FixedUpdate fica em Edit > Project Settings > Time > Fixed Timestep. O padrão é 0,02 segundo, ou seja, 50 passos de física por segundo, conforme a referência de FixedUpdate no Unity 6.3. Pelo código, o mesmo valor é Time.fixedDeltaTime.
Quando mudar:
- Diminuir (0,01, 100 passos por segundo) deixa a física mais precisa: objetos rápidos atravessam menos paredes, empilhamento fica mais estável. Custa CPU, porque a simulação roda o dobro de vezes.
- Aumentar economiza CPU em mobile, mas a física fica mais grosseira e o movimento sem interpolação parece mais travado.
Logo abaixo fica o Maximum Allowed Timestep, que limita quanto tempo de física a engine tenta recuperar num frame lento. Sem esse limite, um travamento gera muitas passadas de física, que deixam o frame seguinte lento também, e o jogo entra numa espiral.
Dentro do FixedUpdate, Time.deltaTime devolve o mesmo valor de Time.fixedDeltaTime (está na referência de Time.deltaTime). Então um timer que usa Time.deltaTime funciona nos dois métodos. Para o conceito de delta time aplicado a movimento, veja delta time e movimento por frame.
Simulation Mode: quando a física não roda no FixedUpdate
A Unity 6.3 permite mudar quando a física simula. O enum SimulationMode tem três valores: FixedUpdate (o padrão, simula logo depois do FixedUpdate), Update (simula depois do Update, com o tempo do frame) e Script (só simula quando você chama Physics.Simulate). A física 2D tem o equivalente em Physics2D.simulationMode, configurável em Project Settings > Physics 2D.
Na prática, quase todo jogo fica no padrão. O modo Script serve para casos especiais, como prever a trajetória de um projétil simulando passos à frente, ou jogo de rede com rollback. Se você mudar o modo, o FixedUpdate continua sendo chamado, mas a física deixa de estar sincronizada com ele, e a regra deste post precisa ser revista.
Quando não usar FixedUpdate
FixedUpdate não é "Update mais preciso". Use só para física. Colocar lógica comum nele traz três problemas:
- Input perdido, como visto acima.
- Movimento travado em objetos sem Rigidbody: se você move um
transformno FixedUpdate, ele só anda 50 vezes por segundo, e num monitor de 144 Hz parece engasgar. - Custo multiplicado em frames lentos: o que está no FixedUpdate pode rodar várias vezes num frame, justamente quando o jogo já está pesado.
Timer de cooldown, IA que decide para onde ir, UI, animação por código, troca de estado: tudo isso fica no Update. Se uma dessas lógicas precisa aplicar uma força, ela decide no Update e deixa um pedido para o FixedUpdate executar, do mesmo jeito que o pulo.
E para objetos que se movem por transform sem física (uma plataforma decorativa, um item flutuando), Update com Time.deltaTime é suficiente.
Próximo passo
Monte uma cena com o PlayerMover, o PlayerJump e a SimpleCameraFollow. Teste com Interpolate ligado e desligado no Rigidbody2D, e troque o PlayerJump pelo BrokenJump com o jogo rodando sem limite de FPS: você vai sentir o pulo falhar na mão. Depois, leia Time.deltaTime na Unity para entender quando multiplicar e quando não multiplicar por delta nos seus Update.
Perguntas frequentes
Quantas vezes por segundo o FixedUpdate roda na Unity?
Por padrão, 50 vezes por segundo, porque o Fixed Timestep padrão é 0,02 segundo (Project Settings > Time). Isso não depende do FPS: num frame lento o FixedUpdate pode rodar duas ou três vezes, e num frame rápido pode não rodar nenhuma.
Posso ler input no FixedUpdate?
Não para ações de um frame só, como apertar pulo. O FixedUpdate pode não rodar no frame em que o botão foi pressionado, e o aperto se perde. Leia o input no Update, guarde numa variável e aplique a física no FixedUpdate.
Para que serve o LateUpdate na Unity?
LateUpdate roda depois de todos os Update do frame. Ele é o lugar para câmera que segue o jogador e para ajustes que precisam do estado final do frame, como alinhar uma barra de vida acima de um inimigo que já se moveu.
Time.deltaTime funciona dentro do FixedUpdate?
Funciona. Dentro do FixedUpdate, a Unity devolve o valor de Time.fixedDeltaTime quando você lê Time.deltaTime. Muita gente usa Time.fixedDeltaTime explicitamente para deixar claro no código que aquele trecho roda no passo da física.



