AnimationTree-Statemachines
in Godot 4
Der vollständige Leitfaden zu Statemachines, Übergängen, Blend Trees, OneShot, travel() vs. Bedingungen — mit echten GDScript-Codebeispielen.
Inhaltsverzeichnis
1. Einführung
AnimationTree ist das System von Godot 4 für komplexes Animation-Blending und Zustandsübergänge. Wenn du jemals versucht hast, mehrere Animationen mit über den gesamten Code verstreuten AnimationPlayer.play()-Aufrufen zu verwalten, weißt du, wie schnell das unübersichtlich wird. AnimationTree löst das mit einem visuellen Graphen aus Zuständen und Übergängen.
Obwohl AnimationTree unglaublich mächtig ist, ist die offizielle Dokumentation dünn und lässt Entwickler oft im Ungewissen. Dieser Leitfaden führt dich durch alles – von der grundlegenden Einrichtung bis zu fortgeschrittenen Blend Trees – mit echtem GDScript-Code, den du direkt in dein Projekt kopieren kannst.
Statemachines, Übergänge, Blend Spaces (1D & 2D), OneShot für Angriffe, travel() vs. Bedingungen und ein vollständiges Character-Controller-Muster.
2. Voraussetzungen
Bevor du deinen AnimationTree einrichtest, brauchst du:
- Einen
AnimationPlayer-Node mit mindestens den bereits erstellten Animationen Idle, Walk, Run und Jump - Der AnimationPlayer muss ein Geschwister- oder Kind-Node des Nodes sein, an dem du den AnimationTree hinzufügst (typischerweise sind beide Kinder deines Charakter-Root-Nodes)
- Godot 4.x (dieser Leitfaden verwendet die Godot-4-API — die AnimationTree-API hat sich gegenüber Godot 3 erheblich verändert)
In Godot 4 wurde AnimationTree.animation_player durch AnimationTree.anim_player ersetzt. Auch der Parameterpfad für die Wiedergabe hat sich geändert. Wenn du von Godot 3 migrierst, sieh dir den offiziellen Migrationsleitfaden an.
3. Grundlegende Einrichtung
Die Einrichtung eines AnimationTree erfolgt in vier Schritten:
-
Einen AnimationTree-Node hinzufügen
— Füge ihn als Kind deines Charakters (z. B.
CharacterBody2DoderCharacterBody3D) neben deinem AnimationPlayer hinzu. -
anim_playerfestlegen — Zeige im Inspector mit der Eigenschaft "Anim Player" auf deinen AnimationPlayer-Node. -
tree_rootfestlegen — Klicke im Inspector auf die Eigenschaft "Tree Root" und erstelle eine neueAnimationNodeStateMachine. -
active = truesetzen — Aktiviere im Inspector das Kontrollkästchen "Active" oder setze es im Code.
Dein Szenenbaum sollte so aussehen:
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
In der Praxis konfigurierst du den AnimationTree fast immer im Editor. Der obige Code ist nur der Vollständigkeit halber angegeben, aber typischerweise brauchst du in deinem Skript nur anim_tree.active = true (und selbst das lässt sich im Editor setzen).
4. Statemachine-Grundlagen
Eine Statemachine im AnimationTree beruht auf einem einfachen Konzept: Zustände repräsentieren Animationen und Übergänge definieren die Bedingungen für den Wechsel zwischen ihnen.
Zustände hinzufügen
Sobald der tree_root deines AnimationTree eine AnimationNodeStateMachine ist, doppelklicke im Inspector darauf, um den Statemachine-Editor zu öffnen:
- Klicke mit der rechten Maustaste in den Graphen-Bereich und wähle Add Animation
- Wähle eine Animation aus deinem AnimationPlayer (Idle, Walk, Run, Jump usw.)
- Wiederhole dies für jeden Animationszustand, den du brauchst
Start ist der Einstiegspunkt — der erste Übergang beginnt immer hier. End ist optional und signalisiert, dass die Statemachine abgeschlossen ist (nützlich für verschachtelte Statemachines).
Übergänge hinzufügen
So erstellst du einen Übergang zwischen zwei Zuständen:
- Klicke auf den Quell-Zustands-Node
- Ziehe zum Ziel-Zustand, um einen Übergangspfeil zu erstellen
- Klicke auf den Übergangspfeil, um ihn im Inspector zu konfigurieren
Übergangseigenschaften
| Eigenschaft | Beschreibung |
|---|---|
advance_mode |
Auto — feuert, wenn die Bedingung wahr ist. Enabled — immer für travel() verfügbar. Disabled — blockiert. |
advance_condition |
Name eines Boolean-Parameters (z. B. is_moving). Wenn wahr, feuert der Übergang automatisch. |
xfade_time |
Crossfade-Dauer in Sekunden. Sanftes Blending zwischen Animationen. Typisch: 0,1 – 0,3 s. |
switch_mode |
Immediate — sofort wechseln. Sync — Wiedergabeposition angleichen. AtEnd — warten, bis die aktuelle Animation endet. |
Eine typische Einrichtung für einen Plattformer-Charakter:
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. Die Statemachine aus Code steuern
Es gibt zwei Hauptwege, die Statemachine anzusteuern: das Setzen von Bedingungsparametern (automatische Übergänge) und der Aufruf von travel() (manuelle Übergänge). Du kannst beide Ansätze kombinieren.
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")
Parameter folgen dem Muster parameters/conditions/<condition_name> für Bedingungen, die auf Übergängen gesetzt sind. Der Bedingungsname muss mit der advance_condition übereinstimmen, die du im Editor am Übergang festgelegt hast.
6. travel() vs. Bedingungen
Bedingungsbasiert (empfohlen für Fortbewegung)
Setze jeden Frame Boolean-Parameter und lass die Übergänge automatisch feuern. Das ist deklarativer und hält deinen Code sauber. Die Statemachine kümmert sich um Übergangslogik, Crossfades und Randfälle für dich.
# 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() (empfohlen für einmalige Aktionen)
travel() fordert einen Zustandsübergang an. Es respektiert die Übergangsregeln — wenn vom aktuellen Zustand kein gültiger Pfad zum Ziel existiert, wird der Aufruf ignoriert. Dadurch ist es sicher, es wiederholt aufzurufen. Verwende es für einmalige Auslöser wie Angriffe, Emotes oder Cutscene-Animationen.
# 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)
Bedingungen für kontinuierliche Zustände (Idle, Walk, Run, Fall). travel() für ereignisgesteuerte Zustände (Angriff, Ausweichen, Interagieren). Viele Projekte nutzen beides: Bedingungen für die Fortbewegung, travel() für Kampfaktionen.
7. Blend Trees
Blend Trees ermöglichen es dir, anhand eines kontinuierlichen Werts sanft zwischen mehreren Animationen zu interpolieren, statt hart zwischen diskreten Zuständen umzuschalten. Das ist ideal für das Blending von Geh-/Laufgeschwindigkeit und für gerichtete Bewegung.
BlendSpace1D
Ein 1D-Blend zwischen zwei oder mehr Animationen entlang einer einzigen Achse. Häufige Verwendung: das Blending von Walk und Run basierend auf der Bewegungsgeschwindigkeit.
Erstelle im Editor einen BlendSpace1D-Node innerhalb deiner Statemachine (oder als eigenständigen Tree Root). Füge Animationspunkte hinzu:
# 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
Ein 2D-Blend mit zwei Achsen. Perfekt für 8-Richtungs-Bewegung oder Top-Down-Spiele, in denen sich der Charakter in jede Richtung bewegen kann.
# 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 unterstützt mehrere Blend-Modi: Die Standard-Triangulation funktioniert in den meisten Fällen gut. Du kannst auch den diskreten Modus wählen (schnappt zum nächstgelegenen Punkt), wenn du Pixel-Art-Animation ohne Interpolation möchtest.
8. Gängige Node-Typen
AnimationTree unterstützt mehrere Node-Typen, die kombiniert werden können, um komplexes Animationsverhalten zu erzeugen:
| Node-Typ | Anwendungsfall |
|---|---|
AnimationNodeStateMachine |
Statemachine mit Übergängen. Der häufigste Root-Node. Zustände können Animationen oder verschachtelte Statemachines sein. |
AnimationNodeBlendSpace1D |
1D-Blend entlang einer Achse. Geh-/Laufgeschwindigkeit, Zielwinkel usw. |
AnimationNodeBlendSpace2D |
2D-Blend mit zwei Achsen. Gerichtete Bewegung, Strafe-Blending. |
AnimationNodeBlendTree |
Ein Graph aus Blend-Operationen. Kombiniere mehrere Blend-Nodes mit eigener Logik. |
AnimationNodeAdd2 |
Additives Blending. Lege eine Animation über eine andere (z. B. einen Ziel-Offset über das Gehen). |
AnimationNodeTimeScale |
Geschwindigkeitssteuerung. Lässt eine Animation zur Laufzeit schneller oder langsamer abspielen. |
AnimationNodeOneShot |
Einmalige Animations-Überlagerung. Perfekt für Angriffe, Emotes, Trefferreaktionen. |
AnimationNodeTransition |
Wechsel zwischen mehreren Eingängen mit Crossfades. Alternative zu Statemachines für einfachere Setups. |
9. OneShot-Muster (Angriffe, Emotes)
Der OneShot-Node ist eines der nützlichsten Muster im AnimationTree. Er spielt eine einmalige Animation über deiner Basisanimation ab (etwa das Abspielen eines Angriffsschwungs während des Gehens) und kehrt anschließend automatisch zur Basisanimation zurück.
Einrichtung im BlendTree
Um OneShot zu verwenden, muss der Root deines AnimationTree (oder ein Zustand darin) ein BlendTree sein:
# 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.
Auslösen aus Code
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
OneShot-Request-Konstanten
| Konstante | Wirkung |
|---|---|
ONE_SHOT_REQUEST_FIRE |
Startet die Wiedergabe der OneShot-Animation |
ONE_SHOT_REQUEST_ABORT |
Bricht den OneShot ab und kehrt sofort zur Basis zurück |
ONE_SHOT_REQUEST_FADE_OUT |
Blendet den OneShot aus (verwendet die Eigenschaft fadeout_time) |
Für Kombosysteme verwende mehrere OneShot-Nodes in Folge oder eine verschachtelte Statemachine im "shot"-Eingang des OneShot mit den Zuständen Attack1 → Attack2 → Attack3.
10. Praxisbeispiel: Vollständiger Character Controller
Hier ist ein vollständiges 2D-Plattformer-Charakterskript, das Statemachine-Fortbewegung mit einem OneShot-Angriff kombiniert. Das ist ein produktionsreifes Muster, das du an dein eigenes Projekt anpassen kannst.
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. Darin: ein StateMachine-Node (mit den Zuständen Idle/Walk/Run/Jump/Fall), verbunden mit einem OneShot-Node ("AttackOneShot"), der wiederum mit dem Output verbunden ist. Die Angriffsanimation ist mit dem "shot"-Eingang des OneShot verbunden.
11. Fehlerbehebung
Prüfe, dass am AnimationTree active = true gesetzt ist und die Eigenschaft anim_player auf einen gültigen AnimationPlayer zeigt. Vergewissere dich außerdem, dass der AnimationPlayer tatsächlich Animationen mit den Namen enthält, die du referenzierst.
Stelle sicher, dass ein gültiger Übergangspfad zwischen dem aktuellen Zustand und dem Zielzustand existiert. travel() schlägt still fehl, wenn kein Pfad existiert. Verwende state_machine.get_current_node(), um zu debuggen, in welchem Zustand du dich tatsächlich befindest.
Prüfe, dass dein blend_position-Wert im Bereich deiner Blend-Space-Punkte liegt. Wenn deine Punkte bei 0.0 und 1.0 liegen, funktioniert ein Wert von 5.0 nicht wie erwartet. Verwende clamp().
Setze active = true entweder im Editor (Kontrollkästchen im Inspector) oder in deiner _ready()-Funktion. Der AnimationTree tut nichts, bis er aktiviert ist.
Prüfe genau, dass: (1) der advance_mode des Übergangs auf Auto gesetzt ist, (2) der Name der advance_condition exakt dem entspricht, was du im Code gesetzt hast (Groß-/Kleinschreibung beachten), und (3) du den Parameter in _physics_process() jeden Frame setzt.
Der AnimationTree kümmert sich ausschließlich um die Animationswiedergabe. Die Bewegungslogik (velocity, move_and_slide()) ist davon getrennt und muss in der _physics_process() deines Skripts implementiert werden.
Willst du deinen AnimationTree von der KI bauen lassen?
Godot MCP Pro kann Statemachines erstellen, Zustände und Übergänge hinzufügen, Blend Trees konfigurieren und Parameter setzen — alles aus einem einzigen Prompt. Sag deinem KI-Assistenten, welches Animationsverhalten du willst, und er baut den kompletten AnimationTree für dich.
- create_animation_tree
- add_state_machine_state
- add_state_machine_transition
- set_blend_tree_node
- set_tree_parameter
- get_animation_tree_structure