AnimationTree State Machine
di Godot 4

Panduan lengkap tentang state machine, transisi, blend tree, OneShot, travel() vs kondisi — dengan contoh kode GDScript nyata.

1. Pendahuluan

AnimationTree adalah sistem Godot 4 untuk blending animasi yang kompleks dan transisi state. Jika kamu pernah mencoba mengelola banyak animasi dengan panggilan AnimationPlayer.play() yang tersebar di seluruh kodemu, kamu tahu betapa cepat hal itu menjadi tak terkendali. AnimationTree menyelesaikannya dengan graf visual berisi state dan transisi.

Meski sangat powerful, dokumentasi resmi AnimationTree terbilang minim dan sering membuat developer menebak-nebak. Panduan ini membahas semuanya – dari penyiapan dasar hingga blend tree tingkat lanjut – dengan kode GDScript nyata yang bisa langsung kamu salin ke proyekmu.

Yang akan kamu pelajari

State machine, transisi, blend space (1D & 2D), OneShot untuk serangan, travel() vs kondisi, dan pola character controller lengkap.

2. Prasyarat

Sebelum menyiapkan AnimationTree-mu, kamu memerlukan:

  • Sebuah node AnimationPlayer dengan setidaknya animasi Idle, Walk, Run, dan Jump yang sudah dibuat
  • AnimationPlayer harus menjadi saudara (sibling) atau anak dari node tempat kamu menambahkan AnimationTree (biasanya keduanya adalah anak dari node root karaktermu)
  • Godot 4.x (panduan ini menggunakan API Godot 4 — API AnimationTree berubah signifikan dibanding Godot 3)
Godot 3 vs Godot 4

Di Godot 4, AnimationTree.animation_player digantikan oleh AnimationTree.anim_player. Path parameter playback juga berubah. Jika kamu bermigrasi dari Godot 3, lihat panduan migrasi resmi.

3. Penyiapan Dasar

Menyiapkan AnimationTree membutuhkan empat langkah:

  1. Tambahkan node AnimationTree — Tambahkan sebagai anak dari karaktermu (mis. CharacterBody2D atau CharacterBody3D), berdampingan dengan AnimationPlayer-mu.
  2. Atur anim_player — Di Inspector, arahkan properti "Anim Player" ke node AnimationPlayer-mu.
  3. Atur tree_root — Klik properti "Tree Root" di Inspector dan buat sebuah AnimationNodeStateMachine baru.
  4. Atur active = true — Centang kotak "Active" di Inspector, atau atur lewat kode.

Scene tree-mu seharusnya terlihat seperti ini:

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 Kode

Dalam praktiknya, kamu hampir selalu mengonfigurasi AnimationTree di editor. Kode di atas ditampilkan demi kelengkapan, tetapi biasanya kamu hanya butuh anim_tree.active = true di skripmu (dan itu pun bisa diatur di editor).

4. Dasar-Dasar State Machine

State machine di AnimationTree bekerja dengan konsep sederhana: state merepresentasikan animasi, dan transisi mendefinisikan kondisi untuk berpindah di antaranya.

Menambahkan State

Setelah tree_root dari AnimationTree-mu berupa AnimationNodeStateMachine, klik dua kali di Inspector untuk membuka editor state machine:

  1. Klik kanan pada area graf dan pilih Add Animation
  2. Pilih sebuah animasi dari AnimationPlayer-mu (Idle, Walk, Run, Jump, dll.)
  3. Ulangi untuk setiap state animasi yang kamu butuhkan
State Khusus

Start adalah titik masuk — transisi pertama selalu dimulai di sini. End bersifat opsional dan menandakan bahwa state machine telah selesai (berguna untuk state machine bersarang).

Menambahkan Transisi

Untuk membuat transisi antara dua state:

  1. Klik pada node state sumber
  2. Seret ke state tujuan untuk membuat panah transisi
  3. Klik panah transisi untuk mengonfigurasinya di Inspector

Properti Transisi

Properti Deskripsi
advance_mode Auto — terpicu ketika kondisinya benar. Enabled — selalu tersedia untuk travel(). Disabled — diblokir.
advance_condition Nama parameter boolean (mis. is_moving). Ketika benar, transisi terpicu secara otomatis.
xfade_time Durasi crossfade dalam detik. Blending mulus antar-animasi. Umumnya: 0,1 – 0,3 dtk.
switch_mode Immediate — berpindah sekarang. Sync — menyamakan posisi playback. AtEnd — menunggu hingga animasi saat ini selesai.

Penyiapan khas untuk karakter platformer:

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. Mengendalikan State Machine dari Kode

Ada dua cara utama untuk menggerakkan state machine: mengatur parameter kondisi (transisi otomatis) dan memanggil travel() (transisi manual). Kamu bisa memadukan kedua pendekatan.

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")
Path Parameter

Parameter mengikuti pola parameters/conditions/<condition_name> untuk kondisi yang diatur pada transisi. Nama kondisi harus cocok dengan advance_condition yang kamu atur pada transisi di editor.

6. travel() vs Kondisi

Berbasis Kondisi (Direkomendasikan untuk Pergerakan)

Atur parameter boolean setiap frame dan biarkan transisi terpicu secara otomatis. Cara ini lebih deklaratif dan menjaga kodemu tetap bersih. State machine menangani logika transisi, crossfade, dan kasus tepi untukmu.

# 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() (Direkomendasikan untuk Aksi Sekali Pakai)

travel() meminta sebuah transisi state. Ia menghormati aturan transisi — jika tidak ada jalur valid dari state saat ini ke target, panggilannya diabaikan. Ini membuatnya aman dipanggil berulang kali. Gunakan untuk pemicu sekali pakai seperti serangan, emote, atau animasi 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)
Kapan Menggunakan yang Mana?

Kondisi untuk state kontinu (idle, walk, run, fall). travel() untuk state yang dipicu kejadian (serangan, mengelak, berinteraksi). Banyak proyek menggunakan keduanya: kondisi untuk pergerakan, travel() untuk aksi pertarungan.

7. Blend Tree

Blend tree memungkinkanmu menginterpolasi secara mulus di antara beberapa animasi berdasarkan nilai kontinu, alih-alih beralih tajam antar-state diskret. Ini sempurna untuk blending kecepatan berjalan/berlari dan pergerakan terarah.

BlendSpace1D

Blend 1D antara dua atau lebih animasi di sepanjang satu sumbu. Penggunaan umum: memadukan Walk dan Run berdasarkan kecepatan gerak.

Di editor, buat sebuah node BlendSpace1D di dalam state machine-mu (atau sebagai tree root berdiri sendiri). Tambahkan titik-titik animasi:

# 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

Blend 2D yang menggunakan dua sumbu. Sempurna untuk pergerakan 8 arah atau game top-down di mana karakter dapat bergerak ke segala arah.

# 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)
Mode Blend

BlendSpace2D mendukung beberapa mode blend: triangulasi default bekerja baik untuk sebagian besar kasus. Kamu juga bisa memilih mode diskret (menyentak ke titik terdekat) jika kamu ingin animasi bergaya pixel art tanpa interpolasi.

8. Tipe Node Umum

AnimationTree mendukung beberapa tipe node yang bisa dikombinasikan untuk menciptakan perilaku animasi yang kompleks:

Tipe Node Kasus Penggunaan
AnimationNodeStateMachine State machine dengan transisi. Node root yang paling umum. State bisa berupa animasi atau state machine bersarang.
AnimationNodeBlendSpace1D Blend 1D di sepanjang satu sumbu. Kecepatan berjalan/berlari, sudut bidik, dll.
AnimationNodeBlendSpace2D Blend 2D menggunakan dua sumbu. Pergerakan terarah, blending strafe.
AnimationNodeBlendTree Graf berisi operasi blend. Gabungkan beberapa node blend dengan logika kustom.
AnimationNodeAdd2 Blending aditif. Tumpuk satu animasi di atas animasi lain (mis. offset bidik di atas berjalan).
AnimationNodeTimeScale Kontrol kecepatan. Membuat animasi diputar lebih cepat atau lebih lambat saat runtime.
AnimationNodeOneShot Overlay animasi sekali pakai. Sempurna untuk serangan, emote, reaksi terkena serangan.
AnimationNodeTransition Beralih antar beberapa input dengan crossfade. Alternatif dari state machine untuk penyiapan yang lebih sederhana.

9. Pola OneShot (Serangan, Emote)

Node OneShot adalah salah satu pola paling berguna di AnimationTree. Ia memutar animasi sekali pakai di atas animasi dasarmu (seperti memainkan ayunan serangan sambil berjalan), lalu otomatis kembali ke animasi dasar.

Penyiapan di BlendTree

Untuk menggunakan OneShot, root AnimationTree-mu (atau state di dalamnya) harus berupa 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.

Memicu dari Kode

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

Konstanta Request OneShot

Konstanta Efek
ONE_SHOT_REQUEST_FIRE Mulai memutar animasi one-shot
ONE_SHOT_REQUEST_ABORT Membatalkan one-shot dan segera kembali ke dasar
ONE_SHOT_REQUEST_FADE_OUT Memudarkan one-shot (menggunakan properti fadeout_time)
Serangan Beruntun

Untuk sistem kombo, gunakan beberapa node OneShot secara berurutan atau state machine bersarang di dalam input "shot" milik OneShot dengan state Attack1 → Attack2 → Attack3.

10. Contoh Praktis: Character Controller Lengkap

Berikut adalah skrip karakter platformer 2D lengkap yang menggabungkan pergerakan berbasis state machine dengan serangan OneShot. Ini adalah pola siap-produksi yang bisa kamu sesuaikan dengan proyekmu sendiri.

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
        )
Struktur AnimationTree untuk Contoh Ini

Root = BlendTree. Di dalamnya: sebuah node StateMachine (dengan state Idle/Walk/Run/Jump/Fall) terhubung ke node OneShot ("AttackOneShot"), yang selanjutnya terhubung ke Output. Animasi serangan terhubung ke input "shot" milik OneShot.

11. Pemecahan Masalah

"Animasi tidak diputar"

Pastikan active = true pada AnimationTree dan properti anim_player mengarah ke AnimationPlayer yang valid. Verifikasi juga bahwa AnimationPlayer benar-benar memiliki animasi dengan nama yang kamu referensikan.

"travel() tidak melakukan apa-apa"

Pastikan ada jalur transisi valid antara state saat ini dan state target. travel() akan gagal secara diam-diam jika tidak ada jalur. Gunakan state_machine.get_current_node() untuk men-debug di state mana kamu sebenarnya berada.

"Blend tidak berfungsi"

Pastikan nilai blend_position-mu berada dalam rentang titik-titik blend space-mu. Jika titikmu berada di 0.0 dan 1.0, nilai 5.0 tidak akan berfungsi seperti yang diharapkan. Gunakan clamp().

"Warning: AnimationTree is not active"

Atur active = true baik di editor (kotak centang di Inspector) atau di fungsi _ready()-mu. AnimationTree tidak melakukan apa-apa sampai diaktifkan.

"Transisi berbasis kondisi tidak terpicu"

Periksa dengan teliti bahwa: (1) advance_mode transisi diatur ke Auto, (2) nama advance_condition persis sama dengan yang kamu atur di kode (peka huruf besar/kecil), dan (3) kamu mengatur parameter tersebut setiap frame di _physics_process().

"Animasi berjalan, tetapi karakter tidak bergerak"

AnimationTree hanya menangani pemutaran animasi. Logika pergerakan (velocity, move_and_slide()) terpisah dan harus diimplementasikan di _physics_process() skripmu.

Ingin AI Membangun AnimationTree-mu?

Godot MCP Pro dapat membuat state machine, menambahkan state dan transisi, mengonfigurasi blend tree, dan mengatur parameter — semuanya dari satu prompt. Beri tahu asisten AI-mu perilaku animasi yang kamu inginkan, dan ia akan membangun seluruh AnimationTree untukmu.

  • create_animation_tree
  • add_state_machine_state
  • add_state_machine_transition
  • set_blend_tree_node
  • set_tree_parameter
  • get_animation_tree_structure
Dapatkan Godot MCP Pro — $15