Voltar para o Blog
Quest Log

Animator na Unity: Animator Controller do Zero

Grafo de estados de animação com caixas Idle, Walk e Jump ligadas por setas ao lado de um personagem em pose de corrida

Unity Animator do zero: Animator Controller com Idle, Walk e Jump, estados, transições, parâmetros e layers, e o mínimo de C# para dirigir as animações.

Ferramenta:UnityC#

Animator na Unity: Animator Controller do Zero

O Unity Animator é o sistema que decide qual animação o personagem toca a cada momento. Você cria um Animator Controller (um asset com uma máquina de estados), coloca nele os clips Idle, Walk e Jump, liga os estados com transições que dependem de parâmetros e, no código, só atualiza esses parâmetros: velocidade, se está no chão, velocidade vertical. O Animator faz o resto.

Este guia monta um Controller de personagem do zero no Unity 6.3 LTS, explica cada peça (estados, Entry, Any State, transições, parâmetros, layers) e entrega o script mínimo em C# que dirige tudo. Os exemplos usam um personagem 2D, mas o Controller funciona igual em 3D.

Animation Clip, Animator e Animator Controller: quem é quem

Três nomes parecidos, três coisas diferentes:

  • Animation Clip (.anim): uma animação. "Andar", "pular", "parado". Guarda keyframes: troca de sprite em 2D, rotação de ossos em 3D, ou qualquer propriedade (cor, posição, escala).
  • Animator Controller (.controller): a máquina de estados. Diz quais clips existem, como passar de um para outro e quais parâmetros decidem isso.
  • Animator (componente): fica no personagem. Aponta para um Controller e toca os clips nos objetos daquele personagem. Em 3D humanoide, também aponta para um Avatar.

Separar o "o quê" (clips) do "quando" (Controller) do "em quem" (componente) é o que permite reaproveitar: o mesmo Controller pode animar dez inimigos, e o Animator Override Controller troca os clips mantendo a mesma lógica. É a mesma ideia de separar responsabilidades que aparece em padrões de projeto para jogos.

Se você vem de animação e quer entender como esse trabalho se divide num estúdio, o post sobre o que faz um animador de jogos mostra onde o Animator Controller entra no pipeline.

Criando o primeiro Animator Controller

Pré-requisito: três clips prontos (Idle, Walk, Jump). Em 2D, um jeito rápido de criar é selecionar os frames de um sprite fatiado e arrastar para a cena: a Unity cria o clip e um Controller automaticamente. Aqui vamos criar o Controller à mão para entender cada parte.

  1. No Project, clique com o botão direito e escolha Create > Animation > Animator Controller. Chame de Player.controller.
  2. No personagem, Add Component > Animator (se ainda não tiver) e arraste o Player.controller para o campo Controller.
  3. Abra Window > Animation > Animator. Com o personagem selecionado, a janela mostra o grafo do Controller.
  4. Arraste os três clips do Project para o grafo. Cada clip vira um estado (uma caixa).

O primeiro estado criado fica laranja: é o estado padrão, o que toca quando o jogo começa. Ele deve ser o Idle. Se não for, clique com o botão direito no Idle e escolha Set as Layer Default State.

Estados, Entry, Any State e transições

No grafo aparecem caixas especiais:

  • Entry: de onde o Controller parte. A seta laranja dele aponta para o estado padrão.
  • Any State: um atalho para "de qualquer estado". Uma transição saindo dele pode interromper qualquer animação. Útil para ações que valem sempre, como levar dano ou morrer.
  • Exit: sai de uma sub-máquina de estados. Num Controller simples, você não usa.

Uma transição é a seta de um estado para outro. Crie clicando com o botão direito no estado de origem, Make Transition, e clicando no destino.

Ao selecionar uma transição, o Inspector mostra:

  • Has Exit Time: se marcado, a transição espera o clip chegar num ponto (Exit Time) antes de acontecer. Para trocas que dependem do jogador (parado para andar, andar para pular), desmarque. Com ele marcado, o personagem continua andando no lugar até o clip chegar no Exit Time, mesmo depois que você soltou o botão.
  • Transition Duration: tempo de mistura entre os dois clips. Em 3D, 0.1 a 0.25 dá suavidade. Em 2D com troca de sprite, use 0: não existe meio-termo entre dois frames de pixel art.
  • Conditions: a lista de condições com parâmetros. Sem condição e sem Exit Time, a transição não tem como disparar (o Inspector avisa).

Transições a criar:

DeParaCondição
IdleWalkSpeed Greater 0.1
WalkIdleSpeed Less 0.1
IdleJumpIsGrounded false
WalkJumpIsGrounded false
JumpIdleIsGrounded true

Uma alternativa é fazer Any State > Jump com IsGrounded false. Se fizer, desmarque Can Transition To Self nessa transição, senão o Jump reinicia a cada frame enquanto o personagem está no ar e a animação trava no primeiro quadro.

Parâmetros e o script que conversa com o Animator

Na janela Animator, aba Parameters, clique em + e crie:

  • Speed (Float): velocidade horizontal absoluta.
  • IsGrounded (Bool): está no chão.
  • VerticalSpeed (Float): sobe ou cai. Serve para trocar entre clip de subida e de queda, se você tiver os dois.

Existem quatro tipos de parâmetro (Float, Int, Bool, Trigger). Trigger é para eventos pontuais, como atacar; para um Controller de locomoção, Float e Bool bastam e evitam o problema de trigger guardado que dispara depois.

O script não decide animação. Ele só informa o estado do personagem ao Animator. Arquivo PlayerAnimator.cs, no personagem:

using UnityEngine;

[RequireComponent(typeof(Animator))]
public class PlayerAnimator : MonoBehaviour
{
    private static readonly int SpeedId = Animator.StringToHash("Speed");
    private static readonly int IsGroundedId = Animator.StringToHash("IsGrounded");
    private static readonly int VerticalSpeedId = Animator.StringToHash("VerticalSpeed");

    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private SpriteRenderer spriteRenderer;
    [SerializeField] private Transform groundCheck;
    [SerializeField] private float groundCheckRadius = 0.1f;
    [SerializeField] private LayerMask groundLayer;

    private Animator animator;

    private void Awake()
    {
        animator = GetComponent<Animator>();
    }

    private void Update()
    {
        Vector2 velocity = rb.linearVelocity;
        bool grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer) != null;

        animator.SetFloat(SpeedId, Mathf.Abs(velocity.x));
        animator.SetBool(IsGroundedId, grounded);
        animator.SetFloat(VerticalSpeedId, velocity.y);

        if (Mathf.Abs(velocity.x) > 0.01f)
        {
            spriteRenderer.flipX = velocity.x < 0f;
        }
    }

    private void OnDrawGizmosSelected()
    {
        if (groundCheck == null)
        {
            return;
        }

        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
    }
}

Pontos importantes:

  • Animator.StringToHash transforma o nome em um id. A documentação recomenda usar o id para otimizar o acesso a parâmetros, e ele concentra os nomes no topo da classe. O nome precisa ser idêntico ao da aba Parameters, com maiúsculas.
  • O script lê o Rigidbody2D (linearVelocity, nome atual no Unity 6) e um ground check com Physics2D.OverlapCircle. Crie um filho vazio GroundCheck nos pés do personagem e uma layer Ground para o chão.
  • O movimento fica em outro script. Este só observa. Assim, trocar a física não quebra a animação e vice-versa.

Montar um Controller de três estados é tranquilo. A dificuldade aparece quando entram ataque, dano, morte, dash e interações, cada um com regras de quem interrompe quem, e o grafo vira uma teia. Organizar isso junto com código e física, num personagem que não trava, é exatamente o tipo de problema que um curso de Unity com projeto completo trabalha passo a passo.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Layers e Avatar Mask: animar só a parte de cima

Layers permitem tocar duas animações ao mesmo tempo em partes diferentes do corpo. O caso clássico: as pernas correm (layer Base) enquanto o tronco atira (layer UpperBody).

  1. Na aba Layers da janela Animator, clique em + e crie UpperBody.
  2. Crie uma Avatar Mask: Create > Animation > Avatar Mask. No Inspector dela, em Humanoid, desmarque pernas e raiz, deixe tronco, braços e cabeça. Para personagem não humanoide, use a seção Transform e marque só os ossos do tronco.
  3. Na engrenagem da layer UpperBody, coloque a máscara em Mask, Weight em 1 e Blending em Override (substitui) ou Additive (soma ao que a Base já faz).
  4. Dentro da layer, crie um estado vazio (sem clip) como padrão e um estado Shoot. Com o estado vazio ativo, a layer não interfere.

Por código, animator.SetLayerWeight(layerIndex, peso) liga e desliga a layer de forma suave, por exemplo subindo o peso enquanto o jogador mira.

Em 2D com troca de sprite, layers servem menos, porque um sprite é uma imagem só. Elas voltam a ser úteis em personagens 2D montados por partes (com o pacote 2D Animation e ossos).

Erros comuns: animação que não toca e personagem travado na pose

A animação não toca. Na ordem: o Animator tem o Controller? O estado padrão é o certo? Abra a janela Animator com o jogo rodando e o personagem selecionado: o estado ativo mostra uma barra de progresso, e os parâmetros mostram os valores atuais. Se o parâmetro não muda, o problema é no script. Se muda e a transição não acontece, é a condição (Greater no lugar de Less é clássico).

A animação toca só uma vez. O clip está sem Loop Time. Selecione o .anim no Project e marque Loop Time no Idle e no Walk. No Jump, normalmente desmarcado.

Personagem travado na pose do primeiro frame. Transição de Any State com Can Transition To Self ligado, reiniciando o estado todo frame.

Animação atrasada ao apertar o botão. Has Exit Time ligado em transições de controle.

Personagem volta a uma pose estranha ou uma propriedade reseta. É o Write Defaults. Cada estado tem essa opção no Inspector; ligada, o estado escreve o valor padrão de propriedades que o Controller anima em outros estados e que ele não anima. Misturar estados com Write Defaults ligado e desligado no mesmo Controller dá resultados difíceis de prever. Escolha um padrão para o projeto inteiro (ligado em todos ou desligado em todos) e mantenha.

Warning "Parameter does not exist". Nome digitado diferente no script e na aba Parameters.

Próximo passo: Blend Tree

O Controller de três estados funciona, mas Walk e Run como estados separados geram troca seca. A Blend Tree mistura vários clips por um parâmetro: com Speed em 0 toca Idle, em 3 toca Walk, em 6 toca Run, e entre esses valores mistura suavemente. Em 2D, uma Blend Tree 2D escolhe a animação pela direção, útil em top-down com oito direções. O guia de Blend Tree na Unity monta as duas versões a partir do Controller que você acabou de criar.

Perguntas frequentes

Qual a diferença entre Animator e Animator Controller na Unity?

O Animator é o componente que fica no personagem e toca as animações. O Animator Controller é o asset com a máquina de estados: quais animações existem, como uma passa para outra e quais parâmetros decidem isso. Um mesmo Controller pode ser usado por vários personagens.

Por que minha animação não toca no Animator da Unity?

Confira se o componente Animator tem o Controller no campo Controller, se o estado padrão (laranja) é o que você espera, se a transição tem condição e se o parâmetro está mudando. Abra a janela Animator com o jogo rodando e selecione o personagem: o estado ativo fica com uma barra de progresso.

Preciso usar Animator.StringToHash?

Não é obrigatório, mas a documentação recomenda usar o id gerado para otimizar a leitura e escrita de parâmetros. Também concentra os nomes num lugar só, o que reduz erro de digitação. Guarde os ids em campos static readonly.

O que é Write Defaults no Animator?

É uma opção de cada estado. Ligada, o estado escreve o valor padrão de qualquer propriedade animada pelo Controller que ele mesmo não anima. Misturar estados com a opção ligada e desligada no mesmo Controller costuma causar propriedades que voltam ou travam sem motivo aparente. Escolha um padrão e mantenha.