Controlling game character animation in Godot 4 requires more than AnimationPlayer — the moment you need blended locomotion, directional movement, or state-driven transitions, you need AnimationTree. This guide walks through the complete setup: building a StateMachine, configuring BlendSpace2D for directional movement, setting transition conditions via GDScript, and importing GLB animations cleanly from Blender.
AnimationTree vs AnimationPlayer: which to use
AnimationPlayer plays individual animation clips. It handles simple cases well — a door opening, a platform moving, a UI fade. But for a character with idle, walk, run, jump, and attack states that blend together based on velocity and input, AnimationPlayer alone becomes unmanageable.
AnimationTree is a node that sits on top of AnimationPlayer and routes playback through a graph of logic nodes:
- StateMachine — drives transitions between named states via conditions
- BlendSpace1D / BlendSpace2D — interpolates between animations based on one or two float inputs
- BlendTree — mixes animations with adjustable weights
- Transition — simple crossfade between two clips
![]()
You can combine these: a StateMachine at the root with each state containing a BlendSpace2D for locomotion, a separate state for jumping, and a transition to an attack state. The hierarchy mirrors how a real character animation system works.
Setup basics:
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var anim_player: AnimationPlayer = $AnimationPlayer
func _ready() -> void:
anim_tree.active = true
anim_tree["parameters/StateMachine/current_node"] = "Idle"
Set AnimationTree.anim_player in the inspector to point at your AnimationPlayer. Enable active either in the inspector or via code.
Setting up a StateMachine in Godot 4
In the AnimationTree panel (bottom editor area), double-click the node to open the graph view. Add a StateMachine node as the root (right-click → Add Node → StateMachine).
Inside the StateMachine:
- Add states by right-clicking → Add Node → Animation and naming each one (
Idle,Walk,Run,Jump,Attack) - Connect states with transition arrows (hover a state, then drag from the arrow that appears)
- Set the Start node to your initial state
Each transition has:
- Switch Mode:
Immediate(cuts instantly),Sync(waits for sync point),At End(waits for clip end) - Auto Advance: triggers the transition when its condition becomes true
- Conditions: boolean or float comparisons you drive from code
To add a condition:
func _physics_process(delta: float) -> void:
var velocity := CharacterBody3D(owner).velocity
var speed := velocity.length()
anim_tree["parameters/StateMachine/conditions/is_moving"] = speed > 0.1
anim_tree["parameters/StateMachine/conditions/is_jumping"] = not CharacterBody3D(owner).is_on_floor()
In the StateMachine editor, on the transition from Idle → Walk, enable Auto Advance and set Condition to is_moving. The transition fires automatically once the condition is true.
BlendSpace2D for directional movement
BlendSpace2D maps two float inputs — typically horizontal and vertical velocity — to a 2D grid of animation clips. Godot interpolates between the nearest clips based on the current parameter values.
Inside the locomotion state, replace the Animation node with a BlendSpace2D:
- Open the
BlendSpace2Deditor - Set X Axis label to
velocity_x, range-1to1 - Set Y Axis label to
velocity_z, range-1to1 - Add animation points at the corners and center:
(0, 0)→ Idle(0, 1)→ Walk Forward(0, -1)→ Walk Backward(-1, 0)→ Strafe Left(1, 0)→ Strafe Right
Godot triangulates the grid automatically. Drive the blend position from code:
func _physics_process(delta: float) -> void:
var local_vel := owner.global_transform.basis.inverse() * CharacterBody3D(owner).velocity
var blend_pos := Vector2(local_vel.x, local_vel.z).limit_length(1.0)
anim_tree["parameters/StateMachine/locomotion/BlendSpace2D/blend_position"] = blend_pos
Use local_vel (velocity in local space) so strafing maps to the correct animation axis regardless of character rotation.
![]()
Blend modes: The default Interpolated mode trilinearly blends between the three nearest points. Discrete switches abruptly to the nearest point — useful for 8-directional movement without foot-sliding artefacts. Carry interpolates but remembers the last blend position, reducing jitter when the character stops.
Importing GLB animations and connecting to AnimationTree
When exporting from Blender for Godot 4, use these GLB settings:
Format: glTF 2.0 (.glb)
Include → Animations: ✓
NLA Tracks: Push Down Actions Before Export
Rest & Ranges → Always Export Bones: ✓
Armature → Add Leaf Bones: ✗ (Godot adds its own)
On import into Godot 4:
- Select the
.glbin the FileSystem panel - Open Import tab → Animation → set
ImporttoAll Clips - Under Animation → Clips, rename animations to match your state names (
Idle,Walk_Forward,Run) - Hit Reimport
The imported animations live inside the .glb resource. Reference them in AnimationPlayer by their clip name. Name your Blender NLA strips exactly what you want the Godot clip to be called — Godot preserves the strip name on import.
Set Loop Mode per clip in the Import tab:
| Clip type | Loop Mode |
|---|---|
| Idle, Walk, Run | Loop |
| Jump, Land, Attack | No Loop |
| Death | No Loop |
| Hit reaction | No Loop |
Clips with wrong loop settings cause the StateMachine to hold or cut unexpectedly at At End transitions.
Pre-wiring checklist
- Armature scale is 1.0, 1.0, 1.0 in Godot scene (apply scale in Blender before export)
- No
.001bone name suffixes (check Blender outliner for duplicates) - Clip count matches your Blender NLA strip count
- Loop flags set correctly per clip type
AnimationTree.anim_playerpoints at the correctAnimationPlayernodeAnimationTree.activeistrueat runtime
Practical tips
- Nested StateMachines: build a top-level machine (Ground / Air / Ragdoll) with sub-machines for each state group. Keeps the graph readable as complexity grows.
- TimeScale parameter: use
parameters/StateMachine/attack/TimeScale/scaleto speed up a single clip without duplicating it. - One-shot animations: use a
OneShotnode inside aBlendTreefor hit reactions — it overlays for the clip duration then returns to the base state automatically. - Root motion: enable
Root Motion TrackinAnimationTreeand readanim_tree.get_root_motion_position()each physics frame to drive movement from animation data rather than from code velocity.
For production Godot 4 projects, browse free rigged characters and animated props at the BitSoul 3D marketplace — all GLB assets include clean bone hierarchies ready to wire into AnimationTree without rework.
This post is part of the Ultimate Guide to Free 3D Game Assets — BitSoul's complete reference for formats, texturing, rigging, optimization, and engine integration.
Build with real assets: grab the 98-model Character Pack on the BitSoul marketplace and drop them straight into your project.