- AnimationTree Mastery
- Expert guidance for Godot's advanced animation blending and state machines.
- NEVER Do
- NEVER call
- play()
- on AnimationPlayer when using AnimationTree
- — AnimationTree controls the player. Directly calling
- play()
- causes conflicts. Use
- set("parameters/transition_request")
- instead.
- NEVER forget to set
- active = true
- — AnimationTree is inactive by default. Animations won't play until
- $AnimationTree.active = true
- .
- NEVER use absolute paths for transition_request
- — Use relative paths. "parameters/StateMachine/transition_request", not "/root/.../transition_request".
- NEVER leave auto_advance enabled unintentionally
- — Auto-advance transitions fire immediately without conditions. Useful for combo chains, but deadly for idle→walk.
- NEVER use BlendSpace2D for non-directional blending
- — Use BlendSpace1D for speed (walk→run) or Blend2 for simple tweens. BlendSpace2D is for X+Y axes (strafe animations).
- Available Scripts
- MANDATORY
- Read the appropriate script before implementing the corresponding pattern. nested_state_machine.gd Hierarchical state machine pattern. Shows travel() between sub-states and deep parameter paths (StateMachine/BlendSpace2D/blend_position). skeleton_ik_lookat.gd Procedural IK for head-tracking. Drives SkeletonModifier3D look-at parameters from AnimationTree with smooth weight blending. Core Concepts AnimationTree Structure AnimationTree (node) ├─ Root (assigned in editor) │ ├─ StateMachine (common) │ ├─ BlendTree (layering) │ └─ BlendSpace (directional) └─ anim_player: NodePath → points to AnimationPlayer Parameter Access
Set parameters using string paths
$AnimationTree . set ( "parameters/StateMachine/transition_request" , "run" ) $AnimationTree . set ( "parameters/Movement/blend_position" , Vector2 ( 1 , 0 ) )
Get current state
var current_state = $AnimationTree . get ( "parameters/StateMachine/current_state" ) StateMachine Pattern Basic Setup
Scene structure:
CharacterBody2D
├─ AnimationPlayer (has: idle, walk, run, jump, land)
└─ AnimationTree
└─ Root: AnimationNodeStateMachine
StateMachine nodes (created in AnimationTree editor):
- Idle (AnimationNode referencing "idle")
- Walk (AnimationNode referencing "walk")
- Run (AnimationNode referencing "run")
- Jump (AnimationNode referencing "jump")
- Land (AnimationNode referencing "land")
@ onready var anim_tree : AnimationTree = $AnimationTree @ onready var state_machine : AnimationNodeStateMachinePlayback = anim_tree . get ( "parameters/StateMachine/playback" ) func _ready ( ) -> void : anim_tree . active = true func _physics_process ( delta : float ) -> void : var velocity := get_velocity ( )
State transitions based on gameplay
if is_on_floor ( ) : if velocity . length ( ) < 10 : state_machine . travel ( "Idle" ) elif velocity . length ( ) < 200 : state_machine . travel ( "Walk" ) else : state_machine . travel ( "Run" ) else : if velocity . y < 0 :
Rising
state_machine . travel ( "Jump" ) else :
Falling
state_machine . travel ( "Land" ) Transition Conditions (Advance Expressions)
In AnimationTree editor:
Add transition from Idle → Walk
Set "Advance Condition" to "is_walking"
In code:
anim_tree . set ( "parameters/conditions/is_walking" , true )
Transition fires automatically when condition becomes true
Useful for event-driven transitions (hurt, dead, etc.)
Example: Damage transition
anim_tree . set ( "parameters/conditions/is_damaged" , false )
Reset each frame
func take_damage ( ) -> void : anim_tree . set ( "parameters/conditions/is_damaged" , true )
Transition to "Hurt" state fires immediately
Auto-Advance (Combo Chains)
In AnimationTree editor:
Transition from Attack1 → Attack2
Enable "Auto Advance" (no condition needed)
Code:
state_machine . travel ( "Attack1" )
Attack1 animation plays
When Attack1 finishes, automatically transitions to Attack2
When Attack2 finishes, transitions to Idle (next auto-advance)
Useful for:
- Attack combos
- Death → Respawn
- Cutscene sequences
BlendSpace2D (Directional Movement) 8-Way Movement
Create BlendSpace2D in AnimationTree editor:
- Add animations at positions:
- (0, -1): walk_up
- (0, 1): walk_down
- (-1, 0): walk_left
- (1, 0): walk_right
- (-1, -1): walk_up_left
- (1, -1): walk_up_right
- (-1, 1): walk_down_left
- (1, 1): walk_down_right
- (0, 0): idle (center)
In code:
func _physics_process ( delta : float ) -> void : var input := Input . get_vector ( "left" , "right" , "up" , "down" )
Set blend position (AnimationTree interpolates between animations)
anim_tree . set ( "parameters/Movement/blend_position" , input )
BlendSpace2D automatically blends animations based on input
input = (0.5, -0.5) → blends walk_right and walk_up
BlendSpace1D (Speed Blending)
For walk → run transitions
Create BlendSpace1D:
- Position 0.0: walk
- Position 1.0: run
func _physics_process ( delta : float ) -> void : var speed := velocity . length ( ) var max_speed := 400.0 var blend_value := clamp ( speed / max_speed , 0.0 , 1.0 ) anim_tree . set ( "parameters/SpeedBlend/blend_position" , blend_value )
Smoothly blends from walk → run as speed increases
BlendTree (Layered Animations) Add Upper Body Animation
Problem: Want to aim gun while walking
Solution: Blend upper body (aim) with lower body (walk)
In AnimationTree editor:
Root → BlendTree
├─ Walk (lower body animation)
├─ Aim (upper body animation)
└─ Add2 node (combines them)
- Inputs: Walk, Aim
- filter_enabled: true
- Filters: Only enable upper body bones for Aim
Code:
No code needed! BlendTree auto-combines
Just ensure animations are assigned
Blend2 (Crossfade)
Blend between two animations dynamically
Root → BlendTree
└─ Blend2
├─ Input A: idle
└─ Input B: attack
Code:
var blend_amount := 0.0 func _process ( delta : float ) -> void :
Gradually blend from idle → attack
blend_amount += delta blend_amount = clamp ( blend_amount , 0.0 , 1.0 ) anim_tree . set ( "parameters/IdleAttackBlend/blend_amount" , blend_amount )
0.0 = 100% idle
0.5 = 50% idle, 50% attack
1.0 = 100% attack
Root Motion with AnimationTree
Enable in AnimationTree
anim_tree . root_motion_track = NodePath ( "CharacterBody3D/Skeleton3D:Root" ) func _physics_process ( delta : float ) -> void :
Get root motion
var root_motion := anim_tree . get_root_motion_position ( )
Apply to character (not velocity!)
global_position += root_motion . rotated ( rotation . y )
For CharacterBody3D with move_and_slide:
velocity
root_motion / delta move_and_slide ( ) Advanced Patterns Sub-StateMachines
Nested state machines for complex behavior
Root → StateMachine
├─ Grounded (Sub-StateMachine)
│ ├─ Idle
│ ├─ Walk
│ └─ Run
└─ Airborne (Sub-StateMachine)
├─ Jump
├─ Fall
└─ Glide
Access nested states:
var sub_state = anim_tree . get ( "parameters/Grounded/playback" ) sub_state . travel ( "Run" ) Time Scale (Slow Motion)
Slow down specific animation without affecting others
anim_tree . set ( "parameters/TimeScale/scale" , 0.5 )
50% speed
Useful for:
- Bullet time
- Hurt/stun effects
- Charge-up animations
Sync Between Animations
Problem: Switching from walk → run causes foot slide
Solution: Use "Sync" on transition
In AnimationTree editor:
Transition: Walk → Run
Enable "Sync" checkbox
Godot automatically syncs animation playback positions
Feet stay grounded during transition
Debugging AnimationTree Print Current State func _process ( delta : float ) -> void : var current_state = anim_tree . get ( "parameters/StateMachine/current_state" ) print ( "Current state: " , current_state )
Print blend position
var blend_pos = anim_tree . get ( "parameters/Movement/blend_position" ) print ( "Blend position: " , blend_pos ) Common Issues
Issue: Animation not playing
Solution:
if not anim_tree . active : anim_tree . active = true
Issue: Transition not working
Check:
1. Is advance_condition set?
2. Is transition priority correct?
3. Is auto_advance enabled unintentionally?
Issue: Blend not smooth
Solution: Increase transition xfade_time (0.1 - 0.3s)
Performance Optimization Disable When Not Needed
AnimationTree is expensive
Disable for off-screen entities
- extends
- VisibleOnScreenNotifier3D
- func
- _ready
- (
- )
- ->
- void
- :
- screen_exited
- .
- connect
- (
- _on_screen_exited
- )
- screen_entered
- .
- connect
- (
- _on_screen_entered
- )
- func
- _on_screen_exited
- (
- )
- ->
- void
- :
- $AnimationTree
- .
- active
- =
- false
- func
- _on_screen_entered
- (
- )
- ->
- void
- :
- $AnimationTree
- .
- active
- =
- true
- Decision Tree: When to Use AnimationTree
- Feature
- AnimationPlayer Only
- AnimationTree
- Simple state swap
- ✅ play("idle")
- ❌ Overkill
- Directional movement
- ❌ Complex
- ✅ BlendSpace2D
- State machine (5+ states)
- ❌ Messy code
- ✅ StateMachine
- Layered animations
- ❌ Manual blending
- ✅ BlendTree
- Root motion
- ✅ Possible
- ✅ Built-in
- Transition blending
- ❌ Manual
- ✅ Auto
- Use AnimationTree for
-
- Complex characters with 5+ states, directional movement, layered animations
- Use AnimationPlayer for
- Simple animations, UI, cutscenes, props Reference Master Skill: godot-master