Quick answer: Setting blend_position directly causes instant snapping. You need to interpolate the value over time using lerp() in _process() or _physics_process() to create smooth transitions between animation states.
Here is how to fix Godot AnimationTree blend not transitioning. Your character snaps abruptly from idle to walk, from walk to run, with no blending in between. You set up an AnimationTree with a BlendSpace2D, configured your animations at the correct positions, and set the blend values from code — but the transitions are instant and jarring instead of the smooth blending you expected. This is one of the most common AnimationTree mistakes in Godot 4.
The Symptom
You have an AnimationTree with a BlendSpace1D or BlendSpace2D that maps animations to positions — idle at 0.0, walk at 0.5, run at 1.0, for instance. In your movement code, you set the blend position based on the character’s speed. The correct animation plays, but there is no smooth transition between them. When you go from standing still to walking, the character pops from the idle pose directly into the walk cycle with no blending frames in between.
In the AnimationTree editor, the blend space preview shows smooth interpolation when you drag the cursor manually. But at runtime, the transitions are sharp and instantaneous, as if blending is disabled entirely. You may also notice that the blend position jumps to its target value in the remote inspector, confirming that no interpolation is happening.
What Causes This
The root cause is almost always that the blend position is being set directly to the target value rather than interpolated toward it over time. When you write animation_tree.set("parameters/BlendSpace1D/blend_position", speed) in _process() or _physics_process(), the value changes instantly to whatever speed is. There is no built-in smoothing — the AnimationTree applies whatever blend position you give it, immediately.
A second cause is misconfigured filter tracks. If you have a blend node that is supposed to blend upper-body animations independently from lower-body animations, but the filter is not enabled or the bone selection is wrong, you get unexpected snapping because the wrong tracks are being overwritten.
A third cause is related to the deterministic property on the AnimationTree. When deterministic is enabled (the default in Godot 4), animations are processed in _physics_process() regardless of where you set the blend values. If you update blend positions in _process() but the tree processes in _physics_process(), you get uneven frame timing that manifests as choppy or snapping transitions, particularly at lower physics tick rates.
The Fix
Step 1: Interpolate the blend position with lerp(). Instead of setting the blend position directly, lerp from the current value to the target value each frame. This creates the smooth transition the AnimationTree needs:
extends CharacterBody3D
@onready var anim_tree = $AnimationTree
var blend_target: float = 0.0
const BLEND_SPEED = 5.0
func _physics_process(delta):
# Calculate target blend from velocity
var speed = velocity.length()
blend_target = clamp(speed / max_speed, 0.0, 1.0)
# Smoothly interpolate toward the target
var current = anim_tree.get("parameters/BlendSpace1D/blend_position")
var smoothed = lerp(current, blend_target, delta * BLEND_SPEED)
anim_tree.set("parameters/BlendSpace1D/blend_position", smoothed)
The BLEND_SPEED constant controls how fast the transition happens. A value of 5.0 gives a responsive but smooth feel. Lower values create more gradual transitions; higher values make them snappier.
Step 2: For BlendSpace2D, interpolate both axes. If you are using a 2D blend space for directional movement, lerp both the X and Y components:
var blend_target_2d: Vector2 = Vector2.ZERO
const BLEND_SPEED = 5.0
func _physics_process(delta):
# Calculate target from movement direction
blend_target_2d = Vector2(
velocity.x / max_speed,
velocity.z / max_speed
)
var current = anim_tree.get("parameters/BlendSpace2D/blend_position")
var smoothed = current.lerp(blend_target_2d, delta * BLEND_SPEED)
anim_tree.set("parameters/BlendSpace2D/blend_position", smoothed)
Step 3: Match processing modes. If your AnimationTree has deterministic enabled (the default), update blend positions in _physics_process(), not _process(). This prevents timing mismatches between when you set the blend value and when the tree evaluates it:
# Check and configure the processing mode
func _ready():
# Option A: Update blends in _physics_process (recommended)
# This matches the default deterministic mode
# Option B: Disable deterministic for visual smoothness
# anim_tree.deterministic = false
# Then update blends in _process() instead
print("Deterministic: ", anim_tree.deterministic)
Step 4: Configure filter tracks for layered blending. If you need upper-body and lower-body to blend independently, enable the filter on the relevant blend node in the AnimationTree editor. Click the node, enable Filter, and check the bones that should be affected by that particular blend. Unchecked bones will pass through from the other input unblended.
Why This Works
The AnimationTree is not an animation state machine with built-in transition smoothing — it is a blend tree that evaluates once per processing frame and produces whatever output the current blend values dictate. If the blend value jumps from 0.0 to 1.0 in a single frame, the output animation jumps too. The tree itself does not add any smoothing.
By interpolating the blend value with lerp(), you spread the transition across multiple frames. The tree sees a gradual change from 0.0 to 0.1 to 0.2 and so on, and the output animation transitions smoothly through the blended intermediate poses. The key insight is that smoothing is your responsibility in code, not the tree’s responsibility.
Matching the processing mode eliminates frame timing issues. When deterministic is on, the tree processes in physics frames (usually 60 per second). If you set the blend value in _process() which runs at the display frame rate, the tree might evaluate with stale or skipped values, producing choppy results.
Related Issues
If the underlying animations in your blend space are imported from Blender and have broken bones, see Fix: Godot Skeletal Animation Import from Blender Breaks Bones.
For AnimationPlayer property tracks that silently fail, which can look like a blend space issue when one animation does nothing, check Fix: AnimationPlayer Property Track Not Updating Node Properties.
If you are trying to use Tweens to drive blend values and the tween system is not working after migration, see Fix: Godot Tween Not Working in Godot 4.
The AnimationTree blends. You smooth. That is the division of labor.