AnimationTree Máquinas de estados
no Godot 4
O guia completo de máquinas de estados, transições, blend trees, OneShot, travel() vs. condições — com exemplos reais de código GDScript.
Índice
- Introdução
- Pré-requisitos
- Configuração básica
- Fundamentos da máquina de estados
- Controlando a máquina de estados via código
- travel() vs. condições
- Blend Trees
- Tipos de node comuns
- Padrão OneShot (ataques, emotes)
- Exemplo prático: character controller completo
- Solução de problemas
- Automatize com o Godot MCP Pro
1. Introdução
O AnimationTree é o sistema do Godot 4 para blending complexo de animações e transições de estado. Se você já tentou gerenciar múltiplas animações com chamadas de AnimationPlayer.play() espalhadas pelo código, sabe como isso rapidamente se torna ingerenciável. O AnimationTree resolve esse problema com um grafo visual de estados e transições.
Apesar de ser incrivelmente poderoso, a documentação oficial do AnimationTree é escassa e muitas vezes deixa os desenvolvedores no escuro. Este guia percorre tudo – da configuração básica aos blend trees avançados – com código GDScript real que você pode copiar direto para o seu projeto.
Máquinas de estados, transições, blend spaces (1D & 2D), OneShot para ataques, travel() vs. condições e um padrão completo de character controller.
2. Pré-requisitos
Antes de configurar o seu AnimationTree, você precisa de:
- Um node
AnimationPlayercom, pelo menos, as animações Idle, Walk, Run e Jump já criadas - O AnimationPlayer deve ser um irmão ou filho do node onde você adiciona o AnimationTree (normalmente ambos são filhos do node raiz do seu personagem)
- Godot 4.x (este guia usa a API do Godot 4 — a API do AnimationTree mudou significativamente em relação ao Godot 3)
No Godot 4, AnimationTree.animation_player foi substituído por AnimationTree.anim_player. O caminho do parâmetro de reprodução também mudou. Se você está migrando do Godot 3, confira o guia de migração oficial.
3. Configuração básica
Configurar um AnimationTree leva quatro passos:
-
Adicione um node AnimationTree
— Adicione-o como filho do seu personagem (ex.:
CharacterBody2DouCharacterBody3D), ao lado do seu AnimationPlayer. -
Defina
anim_player— No Inspector, aponte a propriedade "Anim Player" para o seu node AnimationPlayer. -
Defina
tree_root— Clique na propriedade "Tree Root" no Inspector e crie uma novaAnimationNodeStateMachine. -
Defina
active = true— Marque a caixa "Active" no Inspector ou defina no código.
Sua árvore de cena deve ficar assim:
CharacterBody2D (or CharacterBody3D)
+-- Sprite2D (or Sprite3D)
+-- CollisionShape2D
+-- AnimationPlayer <-- has Idle, Walk, Run, Jump animations
+-- AnimationTree <-- points to AnimationPlayer above
GDScript
# Minimal code setup (usually done via the editor instead):
@onready var anim_tree: AnimationTree = $AnimationTree
func _ready() -> void:
# If you set these in the Inspector, you don't need this code
anim_tree.anim_player = ^"../AnimationPlayer"
anim_tree.tree_root = AnimationNodeStateMachine.new()
anim_tree.active = true
Na prática, você quase sempre configura o AnimationTree no editor. O código acima é mostrado por completude, mas normalmente você só precisa de anim_tree.active = true no seu script (e até isso pode ser definido no editor).
4. Fundamentos da máquina de estados
Uma máquina de estados no AnimationTree funciona com um conceito simples: os estados representam animações e as transições definem as condições para alternar entre elas.
Adicionando estados
Assim que o tree_root do seu AnimationTree for uma AnimationNodeStateMachine, dê duplo clique nele no Inspector para abrir o editor da máquina de estados:
- Clique com o botão direito na área do grafo e selecione Add Animation
- Escolha uma animação do seu AnimationPlayer (Idle, Walk, Run, Jump, etc.)
- Repita para cada estado de animação que você precisar
Start é o ponto de entrada — a primeira transição sempre começa aqui. End é opcional e sinaliza que a máquina de estados terminou (útil para máquinas de estados aninhadas).
Adicionando transições
Para criar uma transição entre dois estados:
- Clique no node do estado de origem
- Arraste até o estado de destino para criar uma seta de transição
- Clique na seta de transição para configurá-la no Inspector
Propriedades da transição
| Propriedade | Descrição |
|---|---|
advance_mode |
Auto — dispara quando sua condição é verdadeira. Enabled — sempre disponível para travel(). Disabled — bloqueada. |
advance_condition |
Nome de um parâmetro booleano (ex.: is_moving). Quando verdadeiro, a transição dispara automaticamente. |
xfade_time |
Duração do crossfade em segundos. Blending suave entre animações. Típico: 0.1 – 0.3s. |
switch_mode |
Immediate — troca agora. Sync — alinha a posição de reprodução. AtEnd — aguarda o fim da animação atual. |
Uma configuração típica para um personagem de plataforma:
Start --> Idle
Idle --> Walk (condition: is_moving)
Walk --> Idle (condition: is_idle)
Idle --> Jump (condition: is_jumping)
Walk --> Jump (condition: is_jumping)
Jump --> Fall (condition: is_falling)
Fall --> Idle (condition: is_on_floor, switch_mode: Immediate)
5. Controlando a máquina de estados via código
Há duas maneiras principais de conduzir a máquina de estados: definindo parâmetros de condição (transições automáticas) e chamando travel() (transições manuais). Você pode combinar as duas abordagens.
extends CharacterBody2D
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var state_machine: AnimationNodeStateMachinePlayback = anim_tree.get("parameters/playback")
func _physics_process(delta: float) -> void:
# ... movement logic here ...
move_and_slide()
_update_animation_parameters()
func _update_animation_parameters() -> void:
# Approach 1: Set condition parameters — transitions fire automatically
anim_tree.set("parameters/conditions/is_moving", velocity.length() > 10.0)
anim_tree.set("parameters/conditions/is_idle", velocity.length() <= 10.0)
anim_tree.set("parameters/conditions/is_on_floor", is_on_floor())
anim_tree.set("parameters/conditions/is_jumping", velocity.y < 0 and not is_on_floor())
anim_tree.set("parameters/conditions/is_falling", velocity.y > 0 and not is_on_floor())
# Approach 2: Use travel() for direct control
# if velocity.length() > 10.0:
# state_machine.travel("Walk")
# else:
# state_machine.travel("Idle")
Os parâmetros seguem o padrão parameters/conditions/<condition_name> para condições definidas em transições. O nome da condição deve corresponder ao advance_condition que você definiu na transição, no editor.
6. travel() vs. condições
Baseado em condições (recomendado para locomoção)
Defina parâmetros booleanos a cada frame e deixe as transições dispararem automaticamente. Isso é mais declarativo e mantém o seu código limpo. A máquina de estados cuida da lógica de transição, dos crossfades e dos casos extremos por você.
# Declarative: just describe the current state of the world
anim_tree.set("parameters/conditions/is_moving", velocity.length() > 10.0)
anim_tree.set("parameters/conditions/is_on_floor", is_on_floor())
travel() (recomendado para ações pontuais)
travel() solicita uma transição de estado. Ele respeita as regras de transição — se não existir um caminho válido do estado atual até o alvo, a chamada é ignorada. Isso o torna seguro para ser chamado repetidamente. Use-o para gatilhos pontuais como ataques, emotes ou animações de cutscene.
# travel() — requests a transition (respects transition rules)
state_machine.travel("Jump")
# Get current state name
var current: StringName = state_machine.get_current_node()
print(current) # "Idle", "Walk", etc.
# Check if travel is possible
var is_playing: bool = state_machine.is_playing()
print(is_playing)
Condições para estados contínuos (idle, walk, run, fall). travel() para estados acionados por eventos (ataque, esquiva, interagir). Muitos projetos usam ambos: condições para a locomoção, travel() para ações de combate.
7. Blend Trees
Os blend trees permitem interpolar suavemente entre várias animações com base em um valor contínuo, em vez de alternar bruscamente entre estados discretos. Isso é ideal para o blending de velocidade de caminhada/corrida e para movimento direcional.
BlendSpace1D
Um blend 1D entre duas ou mais animações ao longo de um único eixo. Uso comum: mesclar Walk e Run com base na velocidade de movimento.
No editor, crie um node BlendSpace1D dentro da sua máquina de estados (ou como tree root autônomo). Adicione pontos de animação:
# BlendSpace1D setup (in editor):
# Point 0.0 = Walk animation
# Point 1.0 = Run animation
# Control from code:
var speed_factor: float = clamp(velocity.length() / max_speed, 0.0, 1.0)
anim_tree.set("parameters/WalkRun/blend_position", speed_factor)
BlendSpace2D
Um blend 2D usando dois eixos. Perfeito para movimento em 8 direções ou jogos top-down em que o personagem pode se mover em qualquer direção.
# BlendSpace2D setup (in editor):
# Place animations at positions:
# Idle at (0, 0)
# WalkRight at (1, 0), WalkLeft at (-1, 0)
# WalkUp at (0, -1), WalkDown at (0, 1)
# Diagonals at corners
# Control from code:
var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
anim_tree.set("parameters/Movement/blend_position", input_dir)
O BlendSpace2D suporta vários modos de blend: a triangulação padrão funciona bem na maioria dos casos. Você também pode escolher o modo discreto (encaixa no ponto mais próximo) se quiser animação estilo pixel art sem interpolação.
8. Tipos de node comuns
O AnimationTree suporta vários tipos de node que podem ser combinados para criar comportamentos de animação complexos:
| Tipo de node | Caso de uso |
|---|---|
AnimationNodeStateMachine |
Máquina de estados com transições. O root node mais comum. Os estados podem ser animações ou máquinas de estados aninhadas. |
AnimationNodeBlendSpace1D |
Blend 1D ao longo de um eixo. Velocidade de caminhada/corrida, ângulo de mira, etc. |
AnimationNodeBlendSpace2D |
Blend 2D usando dois eixos. Movimento direcional, blending de strafe. |
AnimationNodeBlendTree |
Um grafo de operações de blend. Combine vários nodes de blend com lógica personalizada. |
AnimationNodeAdd2 |
Blending aditivo. Sobreponha uma animação a outra (ex.: um offset de mira sobre a caminhada). |
AnimationNodeTimeScale |
Controle de velocidade. Faz uma animação reproduzir mais rápido ou mais devagar em tempo de execução. |
AnimationNodeOneShot |
Sobreposição de animação única. Perfeito para ataques, emotes e reações a golpes. |
AnimationNodeTransition |
Alterna entre várias entradas com crossfades. Alternativa às máquinas de estados para configurações mais simples. |
9. Padrão OneShot (ataques, emotes)
O node OneShot é um dos padrões mais úteis do AnimationTree. Ele reproduz uma animação única por cima da sua animação base (como reproduzir um golpe de ataque enquanto caminha) e, em seguida, retorna automaticamente para a animação base.
Configuração no BlendTree
Para usar o OneShot, o root do seu AnimationTree (ou um estado dentro dele) precisa ser um BlendTree:
# BlendTree graph setup:
#
# [StateMachine] ---> [OneShot "AttackOneShot"] ---> [Output]
# (base locomotion) ^
# |
# [Animation "Attack"]
# (shot input)
#
# The StateMachine provides the base (idle/walk/run).
# The Attack animation is connected to the OneShot's "shot" input.
Acionando via código
GDScriptextends CharacterBody2D
@onready var anim_tree: AnimationTree = $AnimationTree
func _input(event: InputEvent) -> void:
if event.is_action_pressed("attack"):
_play_attack()
func _play_attack() -> void:
# Fire the one-shot animation
anim_tree.set(
"parameters/AttackOneShot/request",
AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
)
func _process(delta: float) -> void:
# Check if the one-shot is currently active
var is_attacking: bool = anim_tree.get("parameters/AttackOneShot/active")
if is_attacking:
# Optionally disable movement during attack
pass
Constantes de request do OneShot
| Constante | Efeito |
|---|---|
ONE_SHOT_REQUEST_FIRE |
Inicia a reprodução da animação OneShot |
ONE_SHOT_REQUEST_ABORT |
Cancela o OneShot e retorna imediatamente para a base |
ONE_SHOT_REQUEST_FADE_OUT |
Faz o fade out do OneShot (usa a propriedade fadeout_time) |
Para sistemas de combo, use vários nodes OneShot em sequência ou uma máquina de estados aninhada na entrada "shot" do OneShot com os estados Attack1 → Attack2 → Attack3.
10. Exemplo prático: character controller completo
Aqui está um script completo de personagem de plataforma 2D que combina locomoção por máquina de estados com um ataque OneShot. Este é um padrão pronto para produção que você pode adaptar ao seu próprio projeto.
GDScript — player.gdextends CharacterBody2D
const SPEED := 200.0
const JUMP_VELOCITY := -350.0
const SPRINT_MULTIPLIER := 1.6
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var playback: AnimationNodeStateMachinePlayback = anim_tree.get("parameters/playback")
@onready var sprite: Sprite2D = $Sprite2D
var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
func _ready() -> void:
anim_tree.active = true
func _physics_process(delta: float) -> void:
_apply_gravity(delta)
_handle_jump()
_handle_movement()
move_and_slide()
_update_animation()
func _apply_gravity(delta: float) -> void:
if not is_on_floor():
velocity.y += gravity * delta
func _handle_jump() -> void:
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
func _handle_movement() -> void:
var direction := Input.get_axis("move_left", "move_right")
var is_sprinting := Input.is_action_pressed("sprint")
var current_speed := SPEED * (SPRINT_MULTIPLIER if is_sprinting else 1.0)
if direction != 0.0:
velocity.x = direction * current_speed
sprite.flip_h = direction < 0.0
else:
velocity.x = move_toward(velocity.x, 0.0, SPEED)
func _update_animation() -> void:
# Skip animation updates during attack
var is_attacking: bool = anim_tree.get("parameters/AttackOneShot/active")
if is_attacking:
return
if not is_on_floor():
if velocity.y < 0:
playback.travel("Jump")
else:
playback.travel("Fall")
elif abs(velocity.x) > 10.0:
if Input.is_action_pressed("sprint"):
playback.travel("Run")
else:
playback.travel("Walk")
else:
playback.travel("Idle")
func _input(event: InputEvent) -> void:
if event.is_action_pressed("attack") and is_on_floor():
anim_tree.set(
"parameters/AttackOneShot/request",
AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
)
Root = BlendTree. Dentro dele: um node StateMachine (com os estados Idle/Walk/Run/Jump/Fall) conectado a um node OneShot ("AttackOneShot"), que por sua vez se conecta ao Output. A animação de ataque se conecta à entrada "shot" do OneShot.
11. Solução de problemas
Verifique se active = true no AnimationTree e se a propriedade anim_player aponta para um AnimationPlayer válido. Confirme também que o AnimationPlayer realmente contém animações com os nomes que você está referenciando.
Certifique-se de que existe um caminho de transição válido entre o estado atual e o estado alvo. travel() falha silenciosamente se nenhum caminho existir. Use state_machine.get_current_node() para depurar em qual estado você realmente está.
Verifique se o seu valor de blend_position está dentro do intervalo dos pontos do seu blend space. Se os seus pontos estão em 0.0 e 1.0, um valor de 5.0 não funcionará como esperado. Use clamp().
Defina active = true no editor (caixa de seleção no Inspector) ou na sua função _ready(). O AnimationTree não faz nada enquanto não for ativado.
Verifique com atenção que: (1) o advance_mode da transição está definido como Auto, (2) o nome do advance_condition corresponde exatamente ao que você definiu no código (diferencia maiúsculas de minúsculas) e (3) você está definindo o parâmetro a cada frame em _physics_process().
O AnimationTree cuida apenas da reprodução de animações. A lógica de movimento (velocity, move_and_slide()) é separada e precisa ser implementada no _physics_process() do seu script.
Quer que a IA construa o seu AnimationTree?
O Godot MCP Pro pode criar máquinas de estados, adicionar estados e transições, configurar blend trees e definir parâmetros — tudo a partir de um único prompt. Diga ao seu assistente de IA qual comportamento de animação você quer e ele constrói o AnimationTree completo para você.
- create_animation_tree
- add_state_machine_state
- add_state_machine_transition
- set_blend_tree_node
- set_tree_parameter
- get_animation_tree_structure