Maszyny stanów AnimationTree
w Godot 4

Kompletny przewodnik po maszynach stanów, przejściach, drzewach mieszania, OneShot, travel() vs warunki — z prawdziwymi przykładami kodu GDScript.

1. Wprowadzenie

AnimationTree to system Godot 4 do złożonego mieszania animacji i przełączania stanów. Jeśli kiedykolwiek próbowałeś zarządzać wieloma animacjami za pomocą wywołań AnimationPlayer.play() rozsianych po całym kodzie, wiesz, jak szybko staje się to niemożliwe do opanowania. AnimationTree rozwiązuje ten problem za pomocą wizualnego grafu stanów i przejść.

Mimo że AnimationTree jest niezwykle potężny, jego oficjalna dokumentacja jest uboga i często pozostawia programistów bez odpowiedzi. Ten przewodnik prowadzi przez wszystko – od podstawowej konfiguracji po zaawansowane drzewa mieszania – z prawdziwym kodem GDScript, który możesz skopiować bezpośrednio do swojego projektu.

Czego się nauczysz

Maszyny stanów, przejścia, przestrzenie mieszania (1D i 2D), OneShot do ataków, travel() vs warunki oraz kompletny wzorzec kontrolera postaci.

2. Wymagania wstępne

Zanim skonfigurujesz swój AnimationTree, potrzebujesz:

  • Węzła AnimationPlayer z już utworzonymi co najmniej animacjami Idle, Walk, Run i Jump
  • AnimationPlayer musi być rodzeństwem lub dzieckiem węzła, do którego dodajesz AnimationTree (zwykle oba są dziećmi głównego węzła postaci)
  • Godot 4.x (ten przewodnik korzysta z API Godot 4 — API AnimationTree znacząco zmieniło się względem Godot 3)
Godot 3 vs Godot 4

W Godot 4 AnimationTree.animation_player został zastąpiony przez AnimationTree.anim_player. Zmieniła się także ścieżka parametru odtwarzania. Jeśli migrujesz z Godot 3, sprawdź oficjalny przewodnik migracji.

3. Podstawowa konfiguracja

Konfiguracja AnimationTree wymaga czterech kroków:

  1. Dodaj węzeł AnimationTree — Dodaj go jako dziecko swojej postaci (np. CharacterBody2D lub CharacterBody3D), obok AnimationPlayer.
  2. Ustaw anim_player — W Inspektorze wskaż właściwością „Anim Player” swój węzeł AnimationPlayer.
  3. Ustaw tree_root — Kliknij właściwość „Tree Root” w Inspektorze i utwórz nową AnimationNodeStateMachine.
  4. Ustaw active = true — Zaznacz pole „Active” w Inspektorze albo ustaw to w kodzie.

Twoje drzewo sceny powinno wyglądać tak:

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
Edytor vs kod

W praktyce niemal zawsze konfigurujesz AnimationTree w edytorze. Powyższy kod pokazano dla kompletności, ale zwykle w skrypcie potrzebujesz jedynie anim_tree.active = true (a i to można ustawić w edytorze).

4. Podstawy maszyny stanów

Maszyna stanów w AnimationTree działa według prostej koncepcji: stany reprezentują animacje, a przejścia definiują warunki przełączania między nimi.

Dodawanie stanów

Gdy tree_root Twojego AnimationTree jest już typu AnimationNodeStateMachine, kliknij go dwukrotnie w Inspektorze, aby otworzyć edytor maszyny stanów:

  1. Kliknij prawym przyciskiem myszy w obszarze grafu i wybierz Add Animation
  2. Wybierz animację ze swojego AnimationPlayer (Idle, Walk, Run, Jump itd.)
  3. Powtórz dla każdego potrzebnego stanu animacji
Stany specjalne

Start to punkt wejścia — pierwsze przejście zawsze zaczyna się tutaj. End jest opcjonalny i sygnalizuje, że maszyna stanów zakończyła działanie (przydatne przy zagnieżdżonych maszynach stanów).

Dodawanie przejść

Aby utworzyć przejście między dwoma stanami:

  1. Kliknij węzeł stanu źródłowego
  2. Przeciągnij do stanu docelowego, aby utworzyć strzałkę przejścia
  3. Kliknij strzałkę przejścia, aby skonfigurować ją w Inspektorze

Właściwości przejścia

Właściwość Opis
advance_mode Auto — wyzwala się, gdy warunek jest prawdziwy. Enabled — zawsze dostępne dla travel(). Disabled — zablokowane.
advance_condition Nazwa parametru logicznego (np. is_moving). Gdy jest prawdziwy, przejście wyzwala się automatycznie.
xfade_time Czas trwania crossfade w sekundach. Płynne mieszanie między animacjami. Typowo: 0,1 – 0,3 s.
switch_mode Immediate — przełącz natychmiast. Sync — dopasuj pozycję odtwarzania. AtEnd — poczekaj, aż bieżąca animacja się zakończy.

Typowa konfiguracja dla postaci z platformówki:

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. Sterowanie maszyną stanów z kodu

Istnieją dwa główne sposoby sterowania maszyną stanów: ustawianie parametrów warunków (automatyczne przejścia) oraz wywoływanie travel() (przejścia ręczne). Oba podejścia można łączyć.

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")
Ścieżki parametrów

Parametry stosują wzorzec parameters/conditions/<condition_name> dla warunków ustawionych na przejściach. Nazwa warunku musi być zgodna z advance_condition, którą ustawiłeś na przejściu w edytorze.

6. travel() vs warunki

Oparte na warunkach (zalecane dla poruszania się)

W każdej klatce ustawiaj parametry logiczne i pozwól, aby przejścia wyzwalały się automatycznie. To bardziej deklaratywne i utrzymuje kod w czystości. Maszyna stanów sama zajmuje się logiką przejść, crossfade'ami i przypadkami brzegowymi.

# 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() (zalecane dla akcji jednorazowych)

travel() żąda przejścia stanu. Respektuje reguły przejść — jeśli z bieżącego stanu nie istnieje prawidłowa ścieżka do celu, wywołanie jest ignorowane. Dzięki temu można je bezpiecznie wywoływać wielokrotnie. Używaj go do jednorazowych wyzwalaczy, takich jak ataki, emotki czy animacje przerywników.

# 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)
Kiedy którego użyć?

Warunki dla stanów ciągłych (bezczynność, chodzenie, bieganie, opadanie). travel() dla stanów wyzwalanych zdarzeniami (atak, unik, interakcja). Wiele projektów używa obu: warunków do poruszania się, travel() do akcji bojowych.

7. Drzewa mieszania (Blend Trees)

Drzewa mieszania pozwalają płynnie interpolować między wieloma animacjami na podstawie wartości ciągłej, zamiast twardo przełączać między dyskretnymi stanami. To idealne rozwiązanie do mieszania prędkości chodu/biegu oraz ruchu kierunkowego.

BlendSpace1D

Mieszanie 1D między dwoma lub więcej animacjami wzdłuż jednej osi. Typowe zastosowanie: mieszanie Walk i Run w zależności od prędkości poruszania się.

W edytorze utwórz węzeł BlendSpace1D wewnątrz swojej maszyny stanów (lub jako samodzielny tree root). Dodaj punkty animacji:

# 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

Mieszanie 2D wykorzystujące dwie osie. Idealne do ruchu 8-kierunkowego lub gier z widokiem z góry, w których postać może poruszać się w dowolnym kierunku.

# 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)
Tryby mieszania

BlendSpace2D obsługuje wiele trybów mieszania: domyślna triangulacja sprawdza się w większości przypadków. Możesz też wybrać tryb dyskretny (przyciąga do najbliższego punktu), jeśli chcesz animację w stylu pixel art bez interpolacji.

8. Popularne typy węzłów

AnimationTree obsługuje kilka typów węzłów, które można łączyć w celu tworzenia złożonego zachowania animacji:

Typ węzła Zastosowanie
AnimationNodeStateMachine Maszyna stanów z przejściami. Najczęstszy węzeł główny. Stany mogą być animacjami lub zagnieżdżonymi maszynami stanów.
AnimationNodeBlendSpace1D Mieszanie 1D wzdłuż jednej osi. Prędkość chodu/biegu, kąt celowania itd.
AnimationNodeBlendSpace2D Mieszanie 2D z dwiema osiami. Ruch kierunkowy, mieszanie ruchu bocznego (strafe).
AnimationNodeBlendTree Graf operacji mieszania. Łącz wiele węzłów mieszania z własną logiką.
AnimationNodeAdd2 Mieszanie addytywne. Nakładaj jedną animację na drugą (np. przesunięcie celowania na chodzenie).
AnimationNodeTimeScale Sterowanie prędkością. Sprawia, że animacja odtwarza się szybciej lub wolniej w czasie działania.
AnimationNodeOneShot Jednorazowa nakładka animacji. Idealna do ataków, emotek, reakcji na trafienia.
AnimationNodeTransition Przełączanie między wieloma wejściami z crossfade'ami. Alternatywa dla maszyn stanów w prostszych konfiguracjach.

9. Wzorzec OneShot (ataki, emotki)

Węzeł OneShot to jeden z najbardziej przydatnych wzorców w AnimationTree. Odtwarza jednorazową animację na wierzchu Twojej animacji bazowej (np. odtwarzanie zamachu atakiem podczas chodzenia), a następnie automatycznie wraca do animacji bazowej.

Konfiguracja w BlendTree

Aby użyć OneShot, root Twojego AnimationTree (lub stan w jego wnętrzu) musi być typu 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.

Wyzwalanie z kodu

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

Stałe żądań OneShot

Stała Efekt
ONE_SHOT_REQUEST_FIRE Rozpocznij odtwarzanie animacji OneShot
ONE_SHOT_REQUEST_ABORT Anuluj OneShot i natychmiast wróć do bazy
ONE_SHOT_REQUEST_FADE_OUT Wyciszaj OneShot (używa właściwości fadeout_time)
Wiele ataków

W przypadku systemów combo użyj kilku węzłów OneShot w sekwencji lub zagnieżdżonej maszyny stanów w wejściu „shot” węzła OneShot ze stanami Attack1 → Attack2 → Attack3.

10. Przykład praktyczny: pełny kontroler postaci

Oto kompletny skrypt postaci 2D do platformówki, który łączy poruszanie się oparte na maszynie stanów z atakiem OneShot. To gotowy do produkcji wzorzec, który możesz dostosować do własnego projektu.

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
        )
Struktura AnimationTree dla tego przykładu

Root = BlendTree. W środku: węzeł StateMachine (ze stanami Idle/Walk/Run/Jump/Fall) połączony z węzłem OneShot („AttackOneShot”), który z kolei łączy się z Output. Animacja ataku łączy się z wejściem „shot” węzła OneShot.

11. Rozwiązywanie problemów

„Animacja się nie odtwarza”

Sprawdź, czy na AnimationTree ustawiono active = true i czy właściwość anim_player wskazuje prawidłowy AnimationPlayer. Upewnij się także, że AnimationPlayer faktycznie zawiera animacje o nazwach, do których się odwołujesz.

„travel() nic nie robi”

Upewnij się, że między bieżącym stanem a stanem docelowym istnieje prawidłowa ścieżka przejścia. travel() cicho zawiedzie, jeśli ścieżka nie istnieje. Użyj state_machine.get_current_node(), aby zdebugować, w jakim stanie faktycznie się znajdujesz.

„Mieszanie nie działa”

Sprawdź, czy wartość blend_position mieści się w zakresie punktów Twojej przestrzeni mieszania. Jeśli punkty znajdują się na 0.0 i 1.0, wartość 5.0 nie zadziała zgodnie z oczekiwaniami. Użyj clamp().

„Warning: AnimationTree is not active”

Ustaw active = true albo w edytorze (pole wyboru w Inspektorze), albo w funkcji _ready(). AnimationTree nic nie robi, dopóki nie zostanie aktywowany.

„Przejście oparte na warunku nie wyzwala się”

Dokładnie sprawdź, czy: (1) advance_mode przejścia jest ustawiony na Auto, (2) nazwa advance_condition dokładnie odpowiada temu, co ustawiłeś w kodzie (z uwzględnieniem wielkości liter), oraz (3) ustawiasz parametr w każdej klatce w _physics_process().

„Animacja się odtwarza, ale postać się nie porusza”

AnimationTree zajmuje się wyłącznie odtwarzaniem animacji. Logika ruchu (velocity, move_and_slide()) jest oddzielna i musi być zaimplementowana w _physics_process() Twojego skryptu.

Chcesz, aby AI zbudowała Twój AnimationTree?

Godot MCP Pro potrafi tworzyć maszyny stanów, dodawać stany i przejścia, konfigurować drzewa mieszania i ustawiać parametry — a wszystko to za pomocą jednego promptu. Powiedz swojemu asystentowi AI, jakiego zachowania animacji oczekujesz, a on zbuduje dla Ciebie cały AnimationTree.

  • create_animation_tree
  • add_state_machine_state
  • add_state_machine_transition
  • set_blend_tree_node
  • set_tree_parameter
  • get_animation_tree_structure
Zdobądź Godot MCP Pro — $15