AnimationTree 狀態機
於 Godot 4
關於狀態機、轉場、Blend Tree、OneShot、travel() 與條件取捨的完整指南 — 附真實 GDScript 程式碼範例。
目錄
1. 簡介
AnimationTree 是 Godot 4 用於複雜動畫混合與狀態轉換的系統。如果你曾經試著用散落在整份程式碼中的 AnimationPlayer.play() 呼叫來管理多個動畫,你就會知道那有多快變得難以維護。AnimationTree 透過一張由狀態與轉場組成的視覺化圖表解決了這個問題。
儘管 AnimationTree 功能極為強大,官方文件卻相當單薄,常常讓開發者摸不著頭緒。本指南會帶你走過一切 – 從基本設定到進階的 Blend Tree – 並附上可直接複製到你專案中的真實 GDScript 程式碼。
狀態機、轉場、Blend Space(1D 與 2D)、用於攻擊的 OneShot、travel() 與條件的取捨,以及一套完整的角色控制器模式。
2. 前置需求
在設定 AnimationTree 之前,你需要:
- 一個至少已建立 Idle、Walk、Run 與 Jump 動畫的
AnimationPlayer節點 - AnimationPlayer 必須是你新增 AnimationTree 的那個節點的同層或子節點(通常兩者都是角色根節點的子節點)
- Godot 4.x(本指南使用 Godot 4 的 API — AnimationTree 的 API 相較於 Godot 3 有大幅變動)
在 Godot 4 中,AnimationTree.animation_player 已被 AnimationTree.anim_player 取代。播放的參數路徑也隨之改變。如果你正從 Godot 3 遷移,請參閱官方遷移指南。
3. 基本設定
設定一個 AnimationTree 分為四個步驟:
-
新增一個 AnimationTree 節點
— 將它與 AnimationPlayer 並列,新增為角色(例如
CharacterBody2D或CharacterBody3D)的子節點。 -
設定
anim_player— 在 Inspector 中將「Anim Player」屬性指向你的 AnimationPlayer 節點。 -
設定
tree_root— 在 Inspector 中點按「Tree Root」屬性,並建立一個新的AnimationNodeStateMachine。 -
設定
active = true— 在 Inspector 中勾選「Active」核取方塊,或於程式碼中設定。
你的場景樹應該長這樣:
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
實務上,你幾乎總是在編輯器中設定 AnimationTree。上面的程式碼只是為了完整性而附上,但通常你的腳本中只需要 anim_tree.active = true(甚至這一項也能在編輯器中設定)。
4. 狀態機基礎
AnimationTree 中的狀態機建立在一個簡單的概念上:狀態(state)代表動畫,而轉場(transition)則定義在狀態間切換的條件。
新增狀態
一旦你的 AnimationTree 的 tree_root 是 AnimationNodeStateMachine,就在 Inspector 中對它按兩下以開啟狀態機編輯器:
- 在圖表區域按右鍵,選擇 Add Animation
- 從你的 AnimationPlayer 中選擇一個動畫(Idle、Walk、Run、Jump 等)
- 對你需要的每個動畫狀態重複此步驟
Start 是進入點 — 第一個轉場永遠從這裡開始。End 是選用的,用來表示狀態機已結束(對巢狀狀態機很有用)。
新增轉場
要在兩個狀態之間建立轉場:
- 點按來源狀態節點
- 拖曳到目標狀態,以建立一個轉場箭頭
- 點按該轉場箭頭,在 Inspector 中設定它
轉場屬性
| 屬性 | 說明 |
|---|---|
advance_mode |
Auto — 條件為真時觸發。Enabled — 始終可供 travel() 使用。Disabled — 封鎖。 |
advance_condition |
一個布林參數的名稱(例如 is_moving)。為真時,轉場會自動觸發。 |
xfade_time |
交叉淡化的秒數。用於動畫之間的平滑混合。典型值:0.1 – 0.3 秒。 |
switch_mode |
Immediate — 立即切換。Sync — 對齊播放位置。AtEnd — 等待目前動畫結束。 |
平台跳躍類角色的典型設定:
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. 從程式碼控制狀態機
驅動狀態機主要有兩種方式:設定條件參數(自動轉場)與呼叫 travel()(手動轉場)。你可以混用這兩種做法。
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")
對於設定在轉場上的條件,參數遵循 parameters/conditions/<condition_name> 的模式。條件名稱必須與你在編輯器中為該轉場設定的 advance_condition 相符。
6. travel() 與條件的取捨
條件式(推薦用於移動)
每一幀設定布林參數,讓轉場自動觸發。這種做法更為宣告式,並讓你的程式碼保持乾淨。狀態機會替你處理轉場邏輯、交叉淡化與各種邊界情況。
# 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()(推薦用於單次動作)
travel() 會請求一次狀態轉換。它會遵守轉場規則 — 如果從目前狀態到目標之間沒有有效路徑,該呼叫就會被忽略。這使得反覆呼叫它是安全的。請把它用於一次性觸發,例如攻擊、表情動作或過場動畫。
# 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)
條件用於持續性的狀態(Idle、Walk、Run、Fall)。travel() 用於由事件觸發的狀態(攻擊、閃避、互動)。許多專案兩者並用:移動用條件,戰鬥動作用 travel()。
7. Blend Tree
Blend Tree 讓你能依據一個連續數值在多個動畫之間平滑地內插,而不是在離散狀態間硬切換。這非常適合用於走路/奔跑速度的混合,以及具方向性的移動。
BlendSpace1D
沿單一軸在兩個或更多動畫之間的 1D 混合。常見用途:依據移動速度混合 Walk 與 Run。
在編輯器中,於你的狀態機內建立一個 BlendSpace1D 節點(或作為獨立的 tree root)。加入動畫點:
# 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
使用兩個軸的 2D 混合。非常適合 8 方向移動,或角色可朝任意方向移動的俯視角遊戲。
# 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 支援多種混合模式:預設的三角剖分在大多數情況下都運作良好。如果你想要不含內插的像素藝術風格動畫,也可以選擇離散模式(吸附到最近的點)。
8. 常見的節點類型
AnimationTree 支援多種可組合的節點類型,用以打造複雜的動畫行為:
| 節點類型 | 使用情境 |
|---|---|
AnimationNodeStateMachine |
帶有轉場的狀態機。最常見的根節點。狀態可以是動畫,也可以是巢狀狀態機。 |
AnimationNodeBlendSpace1D |
沿單一軸的 1D 混合。走路/奔跑速度、瞄準角度等。 |
AnimationNodeBlendSpace2D |
使用兩個軸的 2D 混合。方向性移動、橫向平移(strafe)混合。 |
AnimationNodeBlendTree |
一張由混合運算組成的圖表。以自訂邏輯組合多個混合節點。 |
AnimationNodeAdd2 |
加成式混合。將一個動畫疊加在另一個之上(例如把瞄準偏移疊在走路之上)。 |
AnimationNodeTimeScale |
速度控制。讓動畫在執行時播放得更快或更慢。 |
AnimationNodeOneShot |
單次動畫疊加。非常適合攻擊、表情動作、受擊反應。 |
AnimationNodeTransition |
以交叉淡化在多個輸入之間切換。對較簡單的設定而言是狀態機的替代方案。 |
9. OneShot 模式(攻擊、表情動作)
OneShot 節點是 AnimationTree 中最實用的模式之一。它會在你的基礎動畫之上播放一次性動畫(例如在走路時播放一記攻擊揮擊),接著自動回到基礎動畫。
在 BlendTree 中設定
要使用 OneShot,你的 AnimationTree 根節點(或其中的某個狀態)必須是 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.
從程式碼觸發
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 請求常數
| 常數 | 作用 |
|---|---|
ONE_SHOT_REQUEST_FIRE |
開始播放 OneShot 動畫 |
ONE_SHOT_REQUEST_ABORT |
中止 OneShot 並立即回到基礎動畫 |
ONE_SHOT_REQUEST_FADE_OUT |
淡出 OneShot(使用 fadeout_time 屬性) |
對於連段系統,可依序使用多個 OneShot 節點,或在 OneShot 的「shot」輸入中放入一個帶有 Attack1 → Attack2 → Attack3 狀態的巢狀狀態機。
10. 實戰範例:完整角色控制器
這裡是一份完整的 2D 平台跳躍角色腳本,結合了狀態機移動與一記 OneShot 攻擊。這是一套可用於正式產品的模式,你能依自己的專案加以調整。
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。其中:一個 StateMachine 節點(帶有 Idle/Walk/Run/Jump/Fall 狀態)連到一個 OneShot 節點("AttackOneShot"),後者再連到 Output。攻擊動畫則連到 OneShot 的「shot」輸入。
11. 疑難排解
請檢查 AnimationTree 上是否設定了 active = true,且 anim_player 屬性指向一個有效的 AnimationPlayer。此外,也請確認該 AnimationPlayer 確實含有你所引用之名稱的動畫。
請確保目前狀態與目標狀態之間存在一條有效的轉場路徑。若沒有路徑,travel() 會靜默失敗。使用 state_machine.get_current_node() 來除錯,確認你實際上處於哪個狀態。
請檢查你的 blend_position 值是否落在 blend space 各點的範圍內。若你的點位於 0.0 與 1.0,那麼 5.0 這個值就不會如預期般運作。請使用 clamp()。
請在編輯器中(Inspector 的核取方塊)或你的 _ready() 函式中設定 active = true。AnimationTree 在啟用之前不會做任何事。
請仔細檢查:(1) 該轉場的 advance_mode 設為 Auto,(2) advance_condition 的名稱與你在程式碼中設定的完全相符(區分大小寫),以及 (3) 你在 _physics_process() 中每一幀都有設定該參數。
AnimationTree 只負責動畫播放。移動邏輯(velocity、move_and_slide())是獨立的,必須在你腳本的 _physics_process() 中實作。
想讓 AI 為你打造 AnimationTree 嗎?
Godot MCP Pro 能建立狀態機、新增狀態與轉場、設定 Blend Tree 並設定參數 — 全部只需一段提示。告訴你的 AI 助理你想要的動畫行為,它就會為你打造整棵 AnimationTree。
- create_animation_tree
- add_state_machine_state
- add_state_machine_transition
- set_blend_tree_node
- set_tree_parameter
- get_animation_tree_structure