Voltar para o Blog
Quest Log

AnimationTree no Godot: Máquina de Estados de Animação

Grafo do AnimationTree do Godot conectando estados de idle, andar, correr e pular

AnimationTree no Godot 4: monte uma máquina de estados de animação com transição suave entre idle, andar, correr e pular, use BlendSpace e controle por código.

Quando você anima um personagem só com o AnimationPlayer, a troca de estado é sempre um corte seco: sai do idle, entra no run no mesmo frame, e a mudança fica dura na tela. Funciona pra prototipar, mas não é o que você vê em jogo polido. O AnimationTree no Godot existe pra resolver exatamente isso: ele mistura e transiciona animações em vez de só tocar uma de cada vez, e ainda te dá uma máquina de estados visual pra organizar idle, andar, correr e pular. Nesse tutorial eu monto essa máquina de animação passo a passo e controlo tudo por GDScript tipado.

Antes de continuar, você precisa ter os clipes de animação prontos. Se ainda não domina a base de criar e tocar animações, leia primeiro sobre o AnimationPlayer, porque o AnimationTree depende dele por completo. Aqui eu parto do princípio de que você já tem um AnimationPlayer com as animações idle, walk, run e jump.

AnimationPlayer toca um clipe, AnimationTree mistura vários

A diferença central é essa e vale fixar. O AnimationPlayer é um tocador linear: você chama play("run") e ele executa aquele clipe do começo ao fim. Se você chamar play("idle") no meio, ele corta na hora. Não existe noção de "misturar 30% de idle com 70% de run".

O AnimationTree é uma camada acima. Ele não guarda animação nenhuma; ele lê os clipes de um AnimationPlayer e decide, a cada frame, como combiná-los. Isso abre duas coisas que o player sozinho não faz:

  • Blend (mistura): interpolar entre dois ou mais clipes ao mesmo tempo, gerando poses intermediárias. É o que dá a transição suave de parado pra correndo.
  • Transição com tempo: ao trocar de estado, a árvore faz um cross-fade de X segundos em vez de cortar seco.

Ou seja, o AnimationPlayer responde "qual animação tocar", e o AnimationTree responde "como passar de uma pra outra sem parecer robô". Os dois trabalham juntos, não são concorrentes.

Configurando o AnimationTree apontando pro AnimationPlayer

Adicione um node AnimationTree na sua cena, geralmente irmão do AnimationPlayer, ambos filhos do CharacterBody2D (ou 3D). No Inspector do AnimationTree, duas propriedades importam de cara:

  1. Anim Player: aponte para o seu AnimationPlayer. É daqui que a árvore vai puxar os clipes. Sem isso, ela não tem o que tocar.
  2. Tree Root: escolha o node raiz da árvore. Pra máquina de estados, selecione AnimationNodeStateMachine.

Depois de definir o Tree Root, marque a propriedade Active como ligada. Esse é o erro número um de quem começa: montar a árvore inteira e esquecer o Active desligado, aí nada anima e parece bug. Com o AnimationNodeStateMachine no Tree Root, um painel de edição aparece embaixo, e é onde você desenha os estados.

O AnimationNodeStateMachine: estados e transições

No painel do AnimationTree você vê um grafo em branco. A lógica é parecida com uma máquina de estados de personagem no código, só que aqui os estados são de animação, não de comportamento.

Clique com o botão direito no grafo e adicione um node de animação pra cada clipe. Você vai ter caixas: idle, walk, run, jump. Cada caixa é um estado e reproduz o clipe de mesmo nome do AnimationPlayer.

Agora as transições. Use a ferramenta de conexão (o ícone de seta no topo do painel) e ligue uma caixa na outra. Ligue idle em walk, walk em run, e os dois de volta, e ligue idle/walk/run em jump. Cada seta é uma transição, e ao clicar nela o Inspector mostra as opções que dão o polimento:

  • Xfade Time: o tempo de cross-fade da transição, tipo 0.2. É isso que suaviza a troca.
  • Switch Mode: Immediate corta na hora, Sync mantém a posição do tempo entre os clipes, At End espera o clipe atual terminar. Pra pulo, At End costuma ficar bom; pra locomoção, Sync mantém os pés no ritmo.
  • Advance Mode: deixe em Manual quando você vai disparar a transição por código (nosso caso). O modo Auto com uma condição serve pra transições que a própria árvore decide.

Marque um estado como inicial ligando o node Start (que já vem no grafo) nele. Normalmente o Start vai pro idle.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

BlendSpace1D e BlendSpace2D: mistura por parâmetro

Máquina de estados é ótima pra transições discretas (parado, pulando), mas locomoção é contínua: entre parado e correndo tem todo um gradiente de velocidade. Fazer isso com três estados separados fica robótico. É pra isso que serve o BlendSpace.

Dentro de um estado da máquina, em vez de tocar um clipe único, você pode usar um AnimationNodeBlendSpace1D. Ele tem um eixo (um parâmetro escalar, tipo velocidade) e você posiciona os clipes ao longo dele: idle na posição 0, walk em 100, run em 300. Quando você seta o parâmetro pra 200, a árvore mistura automaticamente walk e run na proporção certa. Zero corte, transição contínua conforme o personagem acelera.

O BlendSpace2D é a mesma ideia com dois eixos. O uso clássico é personagem top-down: eixo X pra direção horizontal, eixo Y pra vertical. Você posiciona os clipes de andar pra cada direção nos pontos do plano (walk_up em (0, -1), walk_right em (1, 0) e assim por diante) e alimenta a árvore com o vetor de direção normalizado. Ela escolhe e mistura a animação certa pro quadrante em que o personagem está indo.

Cada BlendSpace expõe seu parâmetro pra você controlar por código, com um caminho tipo parameters/BlendSpace1D/blend_position. É esse caminho que a gente seta no script.

Controlando por código: playback e travel

Toda a mágica de trocar de estado por script passa pelo objeto de playback da máquina de estados. Você pega ele uma vez e usa travel() pra pedir transições. O travel é diferente de forçar um estado: ele calcula o caminho de conexões até o destino e respeita os tempos de cross-fade que você configurou.

extends CharacterBody2D

const SPEED: float = 300.0
const JUMP_VELOCITY: float = -400.0

@onready var tree: AnimationTree = $AnimationTree
@onready var playback: AnimationNodeStateMachinePlayback = tree["parameters/playback"]

func _physics_process(delta: float) -> void:
    if not is_on_floor():
        velocity += get_gravity() * delta

    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    var direction: float = Input.get_axis("move_left", "move_right")
    velocity.x = direction * SPEED

    _update_animation()
    move_and_slide()

func _update_animation() -> void:
    if not is_on_floor():
        playback.travel("jump")
        return

    var speed: float = absf(velocity.x)
    if speed > 1.0:
        playback.travel("walk")
        tree.set("parameters/walk/BlendSpace1D/blend_position", speed)
    else:
        playback.travel("idle")

Repare em três coisas. Primeiro, o playback vem de tree["parameters/playback"], tipado como AnimationNodeStateMachinePlayback. Segundo, playback.travel("jump") só pede a transição; se você já está em jump, chamar de novo não reinicia nada, então pode chamar todo frame sem medo. Terceiro, o BlendSpace dentro do estado walk é alimentado por tree.set(...) com a velocidade atual, o que mistura andar e correr de forma contínua sem precisar de estados separados pra cada faixa de velocidade.

Se você quiser saber em qual estado está agora, dá pra consultar:

var atual: StringName = playback.get_current_node()
if atual == "jump":
    print("personagem no ar")

Esse padrão casa direto com o loop de movimento. Se você ainda está montando a base de andar e pular, vale ver como estruturar o movimento de um personagem de plataforma 2D antes de plugar a animação por cima, porque o AnimationTree só reage aos valores de velocity e is_on_floor() que esse loop produz.

Quando vale a pena usar AnimationTree

O AnimationTree adiciona setup, então nem todo projeto precisa dele. Uso a régua simples abaixo pra decidir:

  • Vale a pena quando você quer transição suave entre animações, quando a locomoção tem gradiente de velocidade ou direção (aí o BlendSpace brilha), ou quando o personagem tem muitos estados de animação e você quer um grafo visual pra organizar em vez de um monte de play() espalhado.
  • Não compensa num protótipo com duas ou três animações e corte seco aceitável. Nesse caso, chamar animation_player.play("run") direto é mais rápido de montar e mais fácil de depurar.

Uma boa arquitetura separa as duas coisas: a máquina de estados de comportamento (a lógica de "o personagem está atacando ou correndo") decide o que fazer, e o AnimationTree cuida de como aquilo aparece na tela. Você não precisa espelhar um estado de animação pra cada estado de gameplay; o travel é barato de chamar, então deixe o loop de física pedir a animação certa a cada frame e deixe a árvore resolver a mistura.

Com isso você tem o essencial: um AnimationPlayer com os clipes, um AnimationTree ativo apontando pra ele, uma máquina de estados com transições cross-fade, BlendSpaces pra locomoção contínua e um playback controlado por código tipado. A partir daqui é adicionar estados novos (dash, ataque, dano) do mesmo jeito e ir refinando os tempos de transição até o movimento ficar com cara de jogo de verdade.

OK godot-animationtree-maquina-de-animacao

Perguntas frequentes

Qual a diferença entre AnimationPlayer e AnimationTree?

O AnimationPlayer guarda e toca um clipe de animação por vez, com corte seco na troca. O AnimationTree não guarda animação nenhuma: ele lê os clipes de um AnimationPlayer e decide como misturá-los e transicioná-los, gerando blend suave entre estados e mistura contínua por parâmetro.

Preciso de um AnimationPlayer para usar o AnimationTree?

Sim. O AnimationTree é obrigado a apontar para um AnimationPlayer na propriedade Anim Player. Todas as animações usadas na árvore vivem nesse AnimationPlayer; o AnimationTree só orquestra. Sem o player, a árvore não tem clipes para tocar.

Como troco de estado por código no AnimationTree?

Você pega o objeto de playback da máquina de estados com tree["parameters/playback"] e chama playback.travel("nome_do_estado"). O travel respeita as conexões e faz a transição configurada entre o estado atual e o destino, em vez de pular direto.

Para que serve o BlendSpace1D e o BlendSpace2D?

Eles misturam vários clipes ao longo de um ou dois eixos de parâmetro. Um BlendSpace1D mistura idle, andar e correr conforme a velocidade sobe. Um BlendSpace2D usa dois eixos, tipico para direção X e Y em jogos top-down, escolhendo a animação certa por quadrante.

Por que meu AnimationTree não anima nada?

O erro mais comum é esquecer de ativar a propriedade Active do AnimationTree. Com Active desligado a árvore fica parada. Confira também se o Anim Player aponta para o AnimationPlayer certo e se os nomes dos estados batem exatamente com o que você passa no travel.

Quando vale a pena usar AnimationTree em vez de trocar animação na mão?

Vale quando você quer transição suave entre estados, mistura contínua por velocidade ou direção, ou tem muitos estados de animação para organizar. Para um protótipo com duas ou três animações e corte seco aceitável, o AnimationPlayer direto resolve com menos setup.