Voltar para o Blog
Quest Log

Blend Tree na Unity: Andar e Correr sem Corte

Personagem 3D estilizado em transição suave entre andar e correr, com um gráfico de mistura brilhando ao lado

Blend Tree na Unity: misture idle, andar e correr pela velocidade real do personagem, use damping no SetFloat e monte o 2D de 8 direções no Unity 6.3.

Ferramenta:UnityC#

Blend Tree na Unity: Andar e Correr sem Corte

Uma Blend Tree na Unity é um nó do Animator que mistura vários clips de animação ao mesmo tempo, guiado por um parâmetro numérico. Em vez de pular de "andar" para "correr" com uma transição, o personagem passa gradualmente de um para o outro conforme a velocidade sobe. No fim deste tutorial você vai ter uma Blend Tree 1D com idle, walk e run lendo a velocidade real do Rigidbody ou do CharacterController, um SetFloat com damping que tira o tranco do parâmetro e uma Blend Tree 2D para movimento em 8 direções.

O exemplo usa Unity 6.3 LTS, Input System (padrão dos projetos novos) e um personagem humanoide com três clips de locomoção. Se você ainda não tem os clips, o Mixamo resolve isso de graça.

O que é Blend Tree e por que usar no lugar de transições

Com transições, cada mudança de estado tem uma duração fixa. Se o jogador solta o analógico pela metade, o Animator precisa decidir entre "andar" e "correr", e não existe meio termo. O resultado é a animação de corrida tocando numa velocidade de caminhada, ou um corte seco quando a condição muda.

A Blend Tree resolve isso porque trata a locomoção como um espaço contínuo. Com o parâmetro Speed em 0, toca 100% idle. Em 1,5, toca 100% walk. Em 3, mistura metade walk e metade run. O Animator calcula os pesos sozinho, e você só manda um número.

Outro ganho é organização. Uma máquina de estados de locomoção feita só com transições vira uma teia de setas: idle para walk, walk para run, run para idle, cada uma com condição. Com Blend Tree, a locomoção inteira vira um estado só, e as setas ficam para o que realmente é evento: pulo, ataque, dano.

Um detalhe que a documentação da Unity reforça: para a mistura funcionar bem, os movimentos dos clips precisam acontecer nos mesmos pontos do tempo normalizado. Se no clip de andar o pé esquerdo encosta no chão em 0% e no de correr encosta em 50%, a mistura vai embolar as pernas. Clips do mesmo pacote (Mixamo, Asset Store) quase sempre já vêm alinhados. Clips de fontes diferentes, nem sempre.

Blend Tree 1D: idle, andar e correr pela velocidade

No Animator Controller do personagem:

  1. Na aba Parameters, crie um parâmetro Float chamado Speed.
  2. Clique com o botão direito no grafo e escolha Create State > From New Blend Tree. Renomeie o estado para Locomotion e marque como estado padrão (botão direito, Set as Layer Default State).
  3. Dê dois cliques no estado para entrar na Blend Tree. Selecione o nó e, no Inspector, confira que Blend Type está em 1D e que o Parameter é Speed.
  4. Em Motion, clique no + e escolha Add Motion Field três vezes. Arraste Idle, Walk e Run para os campos, nessa ordem.

Agora vem a parte que define se o pé desliza ou não: os thresholds. Por padrão, Automate Thresholds vem ligado e distribui os valores igualmente pelo intervalo do parâmetro. Isso quase nunca bate com o seu jogo.

Desmarque Automate Thresholds e digite os valores na coluna Threshold com a velocidade real, em unidades por segundo, que o personagem tem em cada estado. Se o seu script anda a 1,5 e corre a 4, os thresholds ficam 0, 1,5 e 4. Assim, quando o personagem se move a 1,5 m/s, toca exatamente o clip de andar, sem mistura.

Se os clips têm root motion (os do Mixamo sem "In Place", por exemplo), a lista Compute Thresholds calcula esses números a partir dos dados do clip. Escolha Speed e a Unity preenche cada threshold com a velocidade que a animação realmente percorre. Depois é só igualar a velocidade do seu script a esses valores.

Lendo a velocidade no script e enviando com SetFloat

O erro mais comum é mandar para o Animator o valor do input (de 0 a 1) em vez da velocidade real. Isso funciona até o personagem bater numa parede: o input continua em 1, a velocidade cai para 0 e o boneco corre parado. Leia a velocidade de quem move o corpo.

Com CharacterController, a propriedade velocity já devolve a velocidade do último Move. Arquivo PlayerLocomotion.cs:

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(CharacterController))]
public class PlayerLocomotion : MonoBehaviour
{
    [SerializeField] private Animator animator;
    [SerializeField] private float walkSpeed = 1.5f;
    [SerializeField] private float runSpeed = 4f;
    [SerializeField] private float speedDampTime = 0.1f;

    private CharacterController controller;
    private InputAction moveAction;
    private InputAction sprintAction;
    private static readonly int SpeedHash = Animator.StringToHash("Speed");

    private void Awake()
    {
        controller = GetComponent<CharacterController>();
        moveAction = InputSystem.actions.FindAction("Move");
        sprintAction = InputSystem.actions.FindAction("Sprint");
    }

    private void Update()
    {
        Vector2 input = moveAction.ReadValue<Vector2>();
        float targetSpeed = sprintAction.IsPressed() ? runSpeed : walkSpeed;
        Vector3 direction = new Vector3(input.x, 0f, input.y);

        if (direction.sqrMagnitude > 1f)
        {
            direction.Normalize();
        }

        Vector3 motion = direction * targetSpeed;
        motion.y = -2f; // mantém o controller grudado no chão
        controller.Move(motion * Time.deltaTime);

        if (direction.sqrMagnitude > 0.001f)
        {
            Quaternion look = Quaternion.LookRotation(direction);
            transform.rotation = Quaternion.Slerp(transform.rotation, look, 10f * Time.deltaTime);
        }

        Vector3 horizontal = controller.velocity;
        horizontal.y = 0f;
        animator.SetFloat(SpeedHash, horizontal.magnitude, speedDampTime, Time.deltaTime);
    }
}

As ações Move e Sprint já existem no asset de input padrão que a Unity 6 cria nos projetos novos. Se o seu projeto não tem, crie as duas no Input Actions e marque o asset como Project-wide Actions em Project Settings > Input System Package.

Repare que o y sai do cálculo: a gravidade empurra o controller para baixo o tempo todo, e se ela entrar na conta o personagem "anda" parado. Com Rigidbody, a lógica é a mesma, mas a propriedade mudou de nome no Unity 6: use rb.linearVelocity (o antigo rb.velocity está obsoleto). Leia no Update e zere o y antes de pegar a magnitude:

Vector3 horizontal = rb.linearVelocity;
horizontal.y = 0f;
animator.SetFloat(SpeedHash, horizontal.magnitude, speedDampTime, Time.deltaTime);

O Animator.StringToHash converte o nome do parâmetro em inteiro uma vez só. Além de evitar comparar texto a cada frame, ele concentra o nome num lugar: se você renomear o parâmetro, muda numa linha.

Damping: suavizar o parâmetro sem lógica extra

A sobrecarga SetFloat(int id, float value, float dampTime, float deltaTime) é o detalhe que mais gente ignora. Ela não pula direto para o valor: aproxima o parâmetro do alvo ao longo de dampTime segundos, usando o deltaTime que você passa.

Sem damping, um teclado gera degraus: o Speed vai de 0 para 4 num frame, e a Blend Tree salta de idle para run. Com speedDampTime = 0.1f, a subida leva cerca de um décimo de segundo e o corpo acelera de verdade. Valores que funcionam bem:

  • 0.05 a 0.1: ação, plataforma, qualquer jogo em que o controle precisa responder na hora.
  • 0.15 a 0.25: aventura em terceira pessoa, personagem com peso.
  • Acima de 0.3: começa a parecer que a animação está atrasada em relação ao corpo.

Duas regras: chame o SetFloat com damping todo frame (ele precisa ser atualizado continuamente para andar até o alvo) e sempre passe Time.deltaTime. Se você chamar só quando o input muda, o valor congela no meio do caminho.

Juntar locomoção, input, câmera e um Animator que responde sem tranco num personagem só é onde muita gente que estuda sozinha começa a remendar código de tutoriais diferentes até nada mais conversar. Se você quer montar isso com uma ordem que faz sentido, construindo um jogo inteiro na Unity em vez de peças soltas, o curso foi feito para esse caminho.

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

Blend Tree 2D para movimento em 8 direções e top-down

Quando o personagem anda de lado e de costas sem girar o corpo (mira em terceira pessoa, shooter, top-down), um parâmetro não basta. Crie dois Floats, MoveX e MoveY, e mude o Blend Type da árvore para um dos tipos 2D:

  • 2D Simple Directional: cada clip aponta para uma direção (frente, trás, esquerda, direita), com no máximo um por direção e um idle opcional no centro. Ideal para strafe só caminhando.
  • 2D Freeform Directional: aceita mais de um clip na mesma direção, como andar e correr para a frente. Coloque o idle em (0, 0). É o tipo certo para 8 direções com duas velocidades.
  • 2D Freeform Cartesian: quando os eixos não são direção, por exemplo velocidade linear num eixo e velocidade de giro no outro.

Para 8 direções com Freeform Directional, adicione os clips com Pos X e Pos Y: frente em (0, 1), trás em (0, -1), direita em (1, 0), esquerda em (-1, 0), diagonais em (0,7, 0,7) e variações, e as versões de corrida com o dobro do valor (0, 2) se você quiser que a corrida fique mais longe do centro. Se os clips têm root motion, Compute Positions > Velocity XZ preenche as posições a partir dos dados do clip.

No script, mande a velocidade no espaço local do personagem, não no espaço do mundo. Senão "frente" depende de para onde a câmera olha:

Vector3 localVelocity = transform.InverseTransformDirection(controller.velocity);
animator.SetFloat(MoveXHash, localVelocity.x, 0.1f, Time.deltaTime);
animator.SetFloat(MoveYHash, localVelocity.z, 0.1f, Time.deltaTime);

Em jogo 2D top-down com sprite sheet, a mesma Blend Tree 2D serve para escolher a direção: quatro clips de andar (cima, baixo, esquerda, direita) em Simple Directional, com MoveX e MoveY vindo do input. A mistura entre dois clips de sprite não cria frames intermediários, ela só escolhe o clip mais forte, que é exatamente o que um top-down precisa. Guarde a última direção quando o input zera, para o idle olhar para o lado certo.

Erros comuns: pé deslizando, animação piscando e thresholds errados

Pé deslizando (ice skating). A velocidade do corpo não bate com a do clip. Confira o threshold de cada clip e a velocidade do script. Se o walk foi animado a 1,5 m/s e o script anda a 2, o pé escorrega. Ajuste um dos dois. O post sobre movimento independente de frame com Delta Time ajuda a garantir que a velocidade do script é a mesma em qualquer máquina.

Animação piscando entre idle e walk. O parâmetro oscila perto de zero por ruído do analógico ou da física. Aumente um pouco o damping e aplique uma zona morta no input (o Input System tem o processor Stick Deadzone). Com Rigidbody, ruído de colisão também gera velocidades minúsculas: arredonde para zero abaixo de 0,05.

Personagem correndo parado contra a parede. O script manda o input em vez da velocidade real. Troque para controller.velocity ou rb.linearVelocity.

Pernas cruzando na mistura. Clips com fase diferente. Use clips do mesmo pacote ou ajuste o Cycle Offset do clip na Blend Tree para alinhar o pé de apoio.

Diagonal mais rápida no 2D. O vetor (1, 1) tem magnitude 1,41. Normalize o input antes de multiplicar pela velocidade, como no script acima. O post de vetores na matemática para jogos explica por que isso acontece.

Threshold automático. Se você esqueceu Automate Thresholds ligado e adicionou um quarto clip, todos os valores mudam. Depois de digitar os seus, deixe a opção desmarcada.

Próximo passo

Monte a Blend Tree 1D primeiro, só com idle e walk, e confira o pé no chão em câmera lenta (deixe o Game view aberto e use o slider de velocidade do Animator ou o Time.timeScale). Quando estiver sem deslizar, adicione o run e depois a versão 2D. Para entender estados, camadas e transições que vão rodear essa árvore, leia o guia do Animator Controller do zero.

Perguntas frequentes

Qual a diferença entre Blend Tree e transição no Animator?

A transição troca de um estado para outro em um intervalo fixo de tempo. A Blend Tree mistura vários clips ao mesmo tempo de acordo com um parâmetro contínuo, como a velocidade. Para locomoção, a Blend Tree evita o corte e a combinação de muitas setas entre estados.

Blend Tree 2D: qual tipo escolher?

Use 2D Simple Directional quando cada clip aponta para uma direção diferente e há no máximo um por direção. Use 2D Freeform Directional quando você tem andar e correr na mesma direção. Use 2D Freeform Cartesian quando os dois eixos não representam direção, como velocidade linear e velocidade de giro.

Por que o pé do personagem desliza mesmo com Blend Tree?

Porque a velocidade em que o personagem se move no jogo não bate com a velocidade em que o clip foi animado. Ajuste os thresholds para a velocidade real de cada clip ou ajuste a velocidade de movimento do script para combinar com a animação.

Blend Tree funciona em jogo 2D?

Funciona com qualquer clip, inclusive de sprites. Para sprite sheet, a mistura entre dois clips não gera frames intermediários, então o uso mais comum em 2D é a Blend Tree 2D escolhendo a direção (cima, baixo, lados) num jogo top-down.