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.

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.

Cosa imparerai

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 AnimationPlayer con 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)
Godot 3 vs Godot 4

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:

  1. Aggiungere un nodo AnimationTree — Aggiungilo come figlio del tuo personaggio (es. CharacterBody2D o CharacterBody3D) accanto al tuo AnimationPlayer.
  2. Impostare anim_player — Nell'Inspector, punta la proprietà "Anim Player" al tuo nodo AnimationPlayer.
  3. Impostare tree_root — Clicca sulla proprietà "Tree Root" nell'Inspector e crea una nuova AnimationNodeStateMachine.
  4. 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
Editor vs codice

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:

  1. Fai clic con il tasto destro nell'area del grafo e seleziona Add Animation
  2. Scegli un'animazione dal tuo AnimationPlayer (Idle, Walk, Run, Jump, ecc.)
  3. Ripeti per ogni stato di animazione che ti serve
Stati speciali

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:

  1. Clicca sul nodo dello stato di partenza
  2. Trascina verso lo stato di destinazione per creare una freccia di transizione
  3. 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.

GDScript
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")
Percorsi dei parametri

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)
Quando usare quale?

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)
Modalità di blend

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

GDScript
extends 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)
Attacchi multipli

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.gd
extends 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
        )
Struttura di AnimationTree per questo esempio

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

"L'animazione non viene riprodotta"

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.

"travel() non fa nulla"

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.

"Il blend non funziona"

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().

"Warning: AnimationTree is not active"

Imposta active = true nell'editor (casella di controllo nell'Inspector) oppure nella tua funzione _ready(). L'AnimationTree non fa nulla finché non viene attivato.

"La transizione basata su condizione non scatta"

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'animazione va, ma il personaggio non si muove"

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
Ottieni Godot MCP Pro — $15