Quick answer: Pass a custom_blend to play(), or register blend times via the Animation panel’s “Edit Transitions”. A bare play("name") uses default (often 0) blend.
A character snaps instantly from Idle to Run. Blend times were “set” somewhere but the transition is still a hard cut.
The play() Blend Argument
anim_player.play("run", 0.2) # 0.2s custom blend from current anim
The second argument to play() is the blend time for this transition. Omit it and you get the default — which is 0 unless you configured otherwise.
Default Blend Times Table
In the editor: open the AnimationPlayer, click the Animation menu → “Edit Transitions” (or set_blend_time in code). This stores a per-pair default blend so you don’t need to pass it every play() call:
anim_player.set_blend_time("idle", "run", 0.2)
anim_player.set_blend_time("run", "idle", 0.15)
Consider AnimationTree
For anything beyond simple cross-fades, an AnimationTree with a BlendSpace or StateMachine gives proper, art-directed blending. AnimationPlayer blend times are fine for basic cases.
Don't Re-Play the Same Anim
Calling play() with the animation that’s already playing restarts it. Guard with if anim_player.current_animation != "run": so you don’t re-trigger and visually stutter.
Verifying
Transition Idle ↔ Run. The character cross-fades over the set duration. No hard snap. Re-entering the same state doesn’t restart it.
“Blend time is per-call or per-pair. A bare play() takes the default — which is zero.”
For 3+ animation states with directional movement, jump straight to AnimationTree — manual blend tables get unwieldy fast.