State machine AnimationTree
in Godot 4
La guida completa a state machine, transizioni, blend tree, OneShot, travel() vs condizioni — con esempi reali di codice GDScript.
Indice
- Introduzione
- Prerequisiti
- Configurazione di base
- Fondamenti delle state machine
- Controllare la state machine da codice
- travel() vs condizioni
- Blend tree
- Tipi di nodo comuni
- Pattern OneShot (attacchi, emote)
- Esempio pratico: character controller completo
- Risoluzione dei problemi
- Automatizzare con Godot MCP Pro
1. Introduzione
AnimationTree è il sistema di Godot 4 per il blending complesso delle animazioni e le transizioni di stato. Se hai mai provato a gestire più animazioni con chiamate a AnimationPlayer.play() sparse in tutto il codice, sai quanto rapidamente diventi ingestibile. AnimationTree risolve il problema con un grafo visivo di stati e transizioni.
Nonostante sia incredibilmente potente, la documentazione ufficiale di AnimationTree è scarna e spesso lascia gli sviluppatori a tirare a indovinare. Questa guida ti accompagna attraverso tutto – dalla configurazione di base ai blend tree avanzati – con codice GDScript reale che puoi copiare direttamente nel tuo progetto.
State machine, transizioni, blend space (1D & 2D), OneShot per gli attacchi, travel() vs condizioni e un pattern completo di character controller.
2. Prerequisiti
Prima di configurare il tuo AnimationTree, ti serve:
- Un nodo
AnimationPlayercon almeno le animazioni Idle, Walk, Run e Jump già create - L'AnimationPlayer deve essere un fratello o un figlio del nodo a cui aggiungi l'AnimationTree (di solito entrambi sono figli del nodo radice del tuo personaggio)
- Godot 4.x (questa guida usa l'API di Godot 4 — l'API di AnimationTree è cambiata sensibilmente rispetto a Godot 3)
In Godot 4, AnimationTree.animation_player è stato sostituito da AnimationTree.anim_player. È cambiato anche il percorso del parametro di playback. Se stai migrando da Godot 3, consulta la guida ufficiale alla migrazione.
3. Configurazione di base
La configurazione di un AnimationTree avviene in quattro passi:
-
Aggiungere un nodo AnimationTree
— Aggiungilo come figlio del tuo personaggio (es.
CharacterBody2DoCharacterBody3D) accanto al tuo AnimationPlayer. -
Impostare
anim_player— Nell'Inspector, punta la proprietà "Anim Player" al tuo nodo AnimationPlayer. -
Impostare
tree_root— Clicca sulla proprietà "Tree Root" nell'Inspector e crea una nuovaAnimationNodeStateMachine. -
Impostare
active = true— Attiva la casella "Active" nell'Inspector oppure impostala da codice.
Il tuo albero della scena dovrebbe apparire così:
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
Nella pratica configuri l'AnimationTree quasi sempre nell'editor. Il codice sopra è mostrato per completezza, ma di solito nel tuo script ti serve solo anim_tree.active = true (e anche questo si può impostare nell'editor).
4. Fondamenti delle state machine
Una state machine in AnimationTree si basa su un concetto semplice: gli stati rappresentano le animazioni e le transizioni definiscono le condizioni per passare dall'una all'altra.
Aggiungere gli stati
Una volta che il tree_root del tuo AnimationTree è una AnimationNodeStateMachine, fai doppio clic su di essa nell'Inspector per aprire l'editor della state machine:
- Fai clic con il tasto destro nell'area del grafo e seleziona Add Animation
- Scegli un'animazione dal tuo AnimationPlayer (Idle, Walk, Run, Jump, ecc.)
- Ripeti per ogni stato di animazione che ti serve
Start è il punto di ingresso — la prima transizione parte sempre da qui. End è opzionale e segnala che la state machine è terminata (utile per le state machine annidate).
Aggiungere le transizioni
Per creare una transizione tra due stati:
- Clicca sul nodo dello stato di partenza
- Trascina verso lo stato di destinazione per creare una freccia di transizione
- Clicca sulla freccia di transizione per configurarla nell'Inspector
Proprietà delle transizioni
| Proprietà | Descrizione |
|---|---|
advance_mode |
Auto — scatta quando la condizione è vera. Enabled — sempre disponibile per travel(). Disabled — bloccata. |
advance_condition |
Nome di un parametro booleano (es. is_moving). Quando è vero, la transizione scatta automaticamente. |
xfade_time |
Durata del crossfade in secondi. Blending morbido tra le animazioni. Tipico: 0,1 – 0,3 s. |
switch_mode |
Immediate — cambia subito. Sync — allinea la posizione di playback. AtEnd — attende la fine dell'animazione corrente. |
Una configurazione tipica per un personaggio da platform:
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. Controllare la state machine da codice
Ci sono due modi principali per pilotare la state machine: impostare i parametri di condizione (transizioni automatiche) e chiamare travel() (transizioni manuali). Puoi combinare entrambi gli approcci.
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")
I parametri seguono il pattern parameters/conditions/<condition_name> per le condizioni impostate sulle transizioni. Il nome della condizione deve corrispondere all'advance_condition che hai impostato sulla transizione nell'editor.
6. travel() vs condizioni
Basato su condizioni (consigliato per la locomozione)
Imposta parametri booleani ogni frame e lascia che le transizioni scattino automaticamente. È più dichiarativo e mantiene il codice pulito. La state machine gestisce per te la logica delle transizioni, i crossfade e i casi limite.
# 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() (consigliato per le azioni una tantum)
travel() richiede una transizione di stato. Rispetta le regole di transizione — se non esiste un percorso valido dallo stato corrente alla destinazione, la chiamata viene ignorata. Questo la rende sicura da chiamare ripetutamente. Usala per trigger una tantum come attacchi, emote o animazioni di 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)
Condizioni per gli stati continui (Idle, Walk, Run, Fall). travel() per gli stati guidati da eventi (attacco, schivata, interazione). Molti progetti usano entrambi: le condizioni per la locomozione, travel() per le azioni di combattimento.
7. Blend tree
I blend tree ti permettono di interpolare in modo morbido tra più animazioni in base a un valore continuo, invece di commutare bruscamente tra stati discreti. È l'ideale per il blending della velocità di camminata/corsa e per il movimento direzionale.
BlendSpace1D
Un blend 1D tra due o più animazioni lungo un unico asse. Uso comune: il blending di Walk e Run in base alla velocità di movimento.
Nell'editor, crea un nodo BlendSpace1D dentro la tua state machine (oppure come tree root a sé stante). Aggiungi i punti di animazione:
# 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
Un blend 2D con due assi. Perfetto per il movimento a 8 direzioni o per i giochi top-down in cui il personaggio può muoversi in qualsiasi direzione.
# 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)
BlendSpace2D supporta diverse modalità di blend: la triangolazione predefinita funziona bene nella maggior parte dei casi. Puoi anche scegliere la modalità discreta (scatta al punto più vicino) se vuoi un'animazione in stile pixel art senza interpolazione.
8. Tipi di nodo comuni
AnimationTree supporta diversi tipi di nodo che possono essere combinati per creare comportamenti di animazione complessi:
| Tipo di nodo | Caso d'uso |
|---|---|
AnimationNodeStateMachine |
State machine con transizioni. Il nodo radice più comune. Gli stati possono essere animazioni o state machine annidate. |
AnimationNodeBlendSpace1D |
Blend 1D lungo un asse. Velocità di camminata/corsa, angolo di mira, ecc. |
AnimationNodeBlendSpace2D |
Blend 2D con due assi. Movimento direzionale, blending in strafe. |
AnimationNodeBlendTree |
Un grafo di operazioni di blend. Combina più nodi di blend con la tua logica. |
AnimationNodeAdd2 |
Blending additivo. Sovrapponi un'animazione a un'altra (es. un offset di mira sopra la camminata). |
AnimationNodeTimeScale |
Controllo della velocità. Fa riprodurre un'animazione più veloce o più lenta a runtime. |
AnimationNodeOneShot |
Sovrapposizione di animazione una tantum. Perfetto per attacchi, emote, reazioni ai colpi. |
AnimationNodeTransition |
Passaggio tra più ingressi con crossfade. Alternativa alle state machine per configurazioni più semplici. |
9. Pattern OneShot (attacchi, emote)
Il nodo OneShot è uno dei pattern più utili in AnimationTree. Riproduce un'animazione una tantum sopra la tua animazione di base (come riprodurre un fendente d'attacco durante la camminata) e poi torna automaticamente all'animazione di base.
Configurazione in un BlendTree
Per usare OneShot, la radice del tuo AnimationTree (o uno stato al suo interno) deve essere un 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.
Attivare da codice
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
Costanti di richiesta OneShot
| Costante | Effetto |
|---|---|
ONE_SHOT_REQUEST_FIRE |
Avvia la riproduzione dell'animazione OneShot |
ONE_SHOT_REQUEST_ABORT |
Annulla il OneShot e torna subito alla base |
ONE_SHOT_REQUEST_FADE_OUT |
Dissolve in uscita il OneShot (usa la proprietà fadeout_time) |
Per i sistemi di combo usa più nodi OneShot in sequenza oppure una state machine annidata nell'ingresso "shot" del OneShot con gli stati Attack1 → Attack2 → Attack3.
10. Esempio pratico: character controller completo
Ecco uno script completo per un personaggio da platform 2D che combina la locomozione con state machine e un attacco OneShot. È un pattern pronto per la produzione che puoi adattare al tuo progetto.
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. All'interno: un nodo StateMachine (con gli stati Idle/Walk/Run/Jump/Fall) collegato a un nodo OneShot ("AttackOneShot"), a sua volta collegato all'Output. L'animazione di attacco è collegata all'ingresso "shot" del OneShot.
11. Risoluzione dei problemi
Verifica che sull'AnimationTree sia impostato active = true e che la proprietà anim_player punti a un AnimationPlayer valido. Assicurati inoltre che l'AnimationPlayer contenga effettivamente animazioni con i nomi che stai referenziando.
Assicurati che esista un percorso di transizione valido tra lo stato corrente e lo stato di destinazione. travel() fallisce silenziosamente se non esiste alcun percorso. Usa state_machine.get_current_node() per fare debug e capire in quale stato ti trovi realmente.
Verifica che il valore di blend_position rientri nell'intervallo dei punti del tuo blend space. Se i tuoi punti sono a 0.0 e 1.0, un valore di 5.0 non funzionerà come previsto. Usa clamp().
Imposta active = true nell'editor (casella di controllo nell'Inspector) oppure nella tua funzione _ready(). L'AnimationTree non fa nulla finché non viene attivato.
Controlla con attenzione che: (1) l'advance_mode della transizione sia impostato su Auto, (2) il nome dell'advance_condition corrisponda esattamente a quello che hai impostato nel codice (maiuscole/minuscole comprese) e (3) tu stia impostando il parametro ogni frame in _physics_process().
L'AnimationTree si occupa esclusivamente della riproduzione delle animazioni. La logica di movimento (velocity, move_and_slide()) è separata e deve essere implementata nel _physics_process() del tuo script.
Vuoi che sia l'IA a costruire il tuo AnimationTree?
Godot MCP Pro può creare state machine, aggiungere stati e transizioni, configurare blend tree e impostare parametri — tutto da un singolo prompt. Di' al tuo assistente IA quale comportamento di animazione vuoi e lui costruisce per te l'intero AnimationTree.
- create_animation_tree
- add_state_machine_state
- add_state_machine_transition
- set_blend_tree_node
- set_tree_parameter
- get_animation_tree_structure