Animations in AnimationTree do not blend nicely

Godot Version

4.4.1

Question

In the above screencast the landing animation starts way before landing
How do I fix that?
The controller for the human body is:

[gd_scene load_steps=22 format=3 uid="uid://b3gl4wdkth23s"]

[ext_resource type="Script" uid="uid://bnkug0aurajdx" path="res://human/human.gd" id="1_swqfk"]
[ext_resource type="PackedScene" uid="uid://dtuk11arafcyy" path="res://ui/stat_bar.tscn" id="2_l13k1"]
[ext_resource type="PackedScene" uid="uid://cfekk8478nsk6" path="res://human/human.glb" id="3_g4y02"]
[ext_resource type="Script" uid="uid://cemo1ohqssvwj" path="res://human/animation_tree.gd" id="4_3mfv7"]

[sub_resource type="Gradient" id="Gradient_swqfk"]
colors = PackedColorArray(0, 1, 1, 1, 0, 0.259887, 0.259887, 1)

[sub_resource type="GradientTexture1D" id="GradientTexture1D_l13k1"]
gradient = SubResource("Gradient_swqfk")

[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_swqfk"]
radius = 0.262212
height = 1.83789

[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_3mfv7"]
animation = &"Jump"
use_custom_timeline = true
timeline_length = 0.6
stretch_time_scale = true
start_offset = 0.0
loop_mode = 0

[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_rra3i"]
animation = &"Jump_Land"
use_custom_timeline = true
timeline_length = 0.6
stretch_time_scale = true
start_offset = 0.0
loop_mode = 0

[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_cwuvx"]
animation = &"Jump_Start"
use_custom_timeline = true
timeline_length = 0.6
stretch_time_scale = true
start_offset = 0.0
loop_mode = 0

[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_g4y02"]
animation = &"Idle"

[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_i1to4"]
animation = &"Jog_Fwd"

[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_v0ath"]
animation = &"Walk"

[sub_resource type="AnimationNodeBlendSpace1D" id="AnimationNodeBlendSpace1D_g4y02"]
blend_point_0/node = SubResource("AnimationNodeAnimation_g4y02")
blend_point_0/pos = 0.0
blend_point_1/node = SubResource("AnimationNodeAnimation_i1to4")
blend_point_1/pos = 1.0
blend_point_2/node = SubResource("AnimationNodeAnimation_v0ath")
blend_point_2/pos = 0.5
min_space = 0.0
value_label = "jog_speed"

[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_3mfv7"]
advance_mode = 2

[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_cwuvx"]
switch_mode = 2
advance_mode = 2

[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_vjeuf"]
priority = 3
advance_mode = 2
advance_condition = &"land"

[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_v0ath"]
switch_mode = 2
advance_mode = 2

[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_rra3i"]
advance_mode = 2
advance_condition = &"jump"

[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_rra3i"]
states/Jump/node = SubResource("AnimationNodeAnimation_3mfv7")
states/Jump/position = Vector2(701, 107)
states/Jump_Land/node = SubResource("AnimationNodeAnimation_rra3i")
states/Jump_Land/position = Vector2(474, 187)
states/Jump_Start/node = SubResource("AnimationNodeAnimation_cwuvx")
states/Jump_Start/position = Vector2(520, 39)
states/On_Land/node = SubResource("AnimationNodeBlendSpace1D_g4y02")
states/On_Land/position = Vector2(310, 96)
states/Start/position = Vector2(133, 96)
transitions = ["Start", "On_Land", SubResource("AnimationNodeStateMachineTransition_3mfv7"), "Jump_Land", "On_Land", SubResource("AnimationNodeStateMachineTransition_cwuvx"), "Jump", "Jump_Land", SubResource("AnimationNodeStateMachineTransition_vjeuf"), "Jump_Start", "Jump", SubResource("AnimationNodeStateMachineTransition_v0ath"), "On_Land", "Jump_Start", SubResource("AnimationNodeStateMachineTransition_rra3i")]

[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_3mfv7"]
graph_offset = Vector2(-57.7934, -40.6116)
nodes/LocomotionState/node = SubResource("AnimationNodeStateMachine_rra3i")
nodes/LocomotionState/position = Vector2(200, 80)
nodes/output/position = Vector2(680, 100)
node_connections = [&"output", 0, &"LocomotionState"]

[node name="Human" type="CharacterBody3D"]
script = ExtResource("1_swqfk")

[node name="Stats" type="Control" parent="."]
visible = false
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2

[node name="Stamina" parent="Stats" instance=ExtResource("2_l13k1")]
layout_mode = 1
offset_right = 489.0
max_value = 1000.0
value = 1000.0
texture_progress = SubResource("GradientTexture1D_l13k1")
regenPerS = 15.0

[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.0247744, 0)
shape = SubResource("CapsuleShape3D_swqfk")

[node name="human" parent="." instance=ExtResource("3_g4y02")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.924718, 0)

[node name="Skeleton3D" parent="human/Rig" index="0"]
bones/1/position = Vector3(0.0332124, 0.0501986, 0.916478)
bones/1/rotation = Quaternion(0.790906, -0.000892813, 0.00299126, 0.61193)
bones/3/rotation = Quaternion(-0.0343733, 0.000977367, -0.000637316, 0.999408)
bones/4/rotation = Quaternion(0.128162, 0.00301895, 0.0045672, 0.991738)
bones/5/rotation = Quaternion(0.0253768, -0.00236356, -0.00271943, 0.999671)
bones/6/rotation = Quaternion(-0.0308887, -0.00392792, -0.00648665, 0.999494)
bones/7/rotation = Quaternion(-0.610233, -0.333995, -0.34525, 0.629972)
bones/8/rotation = Quaternion(-0.205862, 0.64088, -0.604188, 0.426438)
bones/9/rotation = Quaternion(0.303459, -0.0625007, -0.0410956, 0.949904)
bones/10/rotation = Quaternion(0.0438946, 0.0182804, -0.0121017, 0.998796)
bones/11/rotation = Quaternion(0.445191, 0.549369, -0.445194, 0.549364)
bones/12/rotation = Quaternion(0.623342, -3.9064e-07, 1.97343e-07, 0.78195)
bones/13/rotation = Quaternion(0.623342, -1.49205e-07, -2.64902e-07, 0.78195)
bones/14/rotation = Quaternion(0.43209, 0.5387, -0.458046, 0.559727)
bones/15/rotation = Quaternion(0.623341, 0.000943295, 0.00118354, 0.781949)
bones/16/rotation = Quaternion(0.623341, -0.000704269, -0.00088337, 0.781949)
bones/17/rotation = Quaternion(0.504057, 0.511541, -0.435152, 0.543042)
bones/18/rotation = Quaternion(0.623163, -0.0200616, 0.0149254, 0.781692)
bones/19/rotation = Quaternion(0.623341, 0.000379915, 0.000476515, 0.78195)
bones/20/rotation = Quaternion(0.43534, 0.541356, -0.454904, 0.557203)
bones/21/rotation = Quaternion(0.623341, 7.29279e-06, 9.38638e-06, 0.78195)
bones/22/rotation = Quaternion(0.623342, -0.000665091, -0.000834377, 0.781949)
bones/23/rotation = Quaternion(0.290838, 0.949649, 0.114584, 0.0212171)
bones/24/rotation = Quaternion(0.21939, 0.0238026, -0.00530494, 0.975332)
bones/25/rotation = Quaternion(0.687388, -0.000309216, -0.000444294, 0.72629)
bones/26/rotation = Quaternion(-0.61865, 0.318134, 0.328876, 0.638673)
bones/27/rotation = Quaternion(-0.230945, -0.644816, 0.587763, 0.430595)
bones/28/rotation = Quaternion(0.320051, 0.0426678, 0.0736286, 0.943571)
bones/29/rotation = Quaternion(0.205644, -0.0105448, 0.00994251, 0.978519)
bones/30/rotation = Quaternion(0.44519, -0.549369, 0.445194, 0.549365)
bones/31/rotation = Quaternion(0.623342, -1.62618e-07, 1.93628e-08, 0.78195)
bones/32/rotation = Quaternion(0.623342, -3.13541e-07, 1.24485e-07, 0.781949)
bones/33/rotation = Quaternion(0.43209, -0.5387, 0.458047, 0.559727)
bones/34/rotation = Quaternion(0.623341, -0.000943381, -0.00118372, 0.781949)
bones/35/rotation = Quaternion(0.623341, 0.000704256, 0.000883413, 0.781949)
bones/36/rotation = Quaternion(0.504057, -0.511541, 0.435152, 0.543042)
bones/37/rotation = Quaternion(0.623163, 0.0200614, -0.0149258, 0.781692)
bones/38/rotation = Quaternion(0.623342, -0.000379869, -0.000476257, 0.781949)
bones/39/rotation = Quaternion(0.435339, -0.541356, 0.454904, 0.557203)
bones/40/rotation = Quaternion(0.623342, -7.72309e-06, -9.38574e-06, 0.78195)
bones/41/rotation = Quaternion(0.623341, 0.000664883, 0.000834436, 0.781949)
bones/42/rotation = Quaternion(-0.290838, 0.949649, 0.114584, -0.0212171)
bones/43/rotation = Quaternion(0.219391, -0.0238026, 0.00530474, 0.975332)
bones/44/rotation = Quaternion(0.687388, 0.000309108, 0.000444535, 0.72629)
bones/45/rotation = Quaternion(0.991991, -0.0226243, 0.00269767, 0.124236)
bones/46/rotation = Quaternion(0.13682, -0.00210498, 0.000243959, 0.990594)
bones/47/rotation = Quaternion(-0.562163, 0.00593061, -0.0141584, 0.826884)
bones/48/rotation = Quaternion(-0.00210185, 0.950347, -0.311184, -0.000923651)
bones/49/rotation = Quaternion(0.898502, -0.0234105, 0.00327813, 0.438332)
bones/50/rotation = Quaternion(0.682526, -0.00970052, 0.00893526, 0.730743)
bones/51/rotation = Quaternion(-0.328464, -0.0104718, -0.0251279, 0.944124)
bones/52/rotation = Quaternion(-0.000137102, 0.964307, -0.264787, -0.000499387)

[node name="AnimationTree" type="AnimationTree" parent="."]
root_node = NodePath("../human")
tree_root = SubResource("AnimationNodeBlendTree_3mfv7")
anim_player = NodePath("../human/AnimationPlayer")
parameters/LocomotionState/conditions/jump = false
parameters/LocomotionState/conditions/land = false
parameters/LocomotionState/On_Land/blend_position = 0.50736
script = ExtResource("4_3mfv7")

[editable path="human"]

Could you share your script that controls the “land” condition?

#animation_tree.gd
extends AnimationTree

@onready var locomotion_state_playback :AnimationNodeStateMachinePlayback = self["parameters/LocomotionState/playback"]
var locomotion_state_jump:bool:
	get():return self["parameters/LocomotionState/conditions/jump"]
	set(v):
		self["parameters/LocomotionState/conditions/jump"]=v
		self["parameters/LocomotionState/conditions/land"]=!v

var locomotion_state_on_land_blend:float:
	get():return self["parameters/LocomotionState/On_Land/blend_position"]
	set(v):self["parameters/LocomotionState/On_Land/blend_position"]=v
@onready var human:Human=$".."
func _ready() -> void:
	locomotion_state_jump = false
func _physics_process(delta: float) -> void:
		locomotion_state_on_land_blend=clampf(human.velocity.length()/10,0,1)
		if $"..".velocity.y>0:locomotion_state_jump = true
		elif $"..".is_on_floor():locomotion_state_jump = false

Can you make sure that is_on_floor() is behaving correctly (and consistently)?

On a side note, I have to ask: why the getters and setters? For example, locomotion_state_on_land_blend in the script above is never assigned a value (except by the compiler), since the setter always shoots that value somewhere else.

#human.gd
class_name Human
extends CharacterBody3D

@export var speed := 10.0
@export var accel := 20.0
@export_category("Wall run")
@export var wall_accel :=15.0
@export var wall_friction := 0.7
@export var wall_run_stickyness := 4.0

func process_movement(i:Vector2,delta:float):
	var input_dir:=Vector3.ZERO if $Stats/Stamina.value == 0 else Vector3(i.x,0,i.y)

	# base movement
	var direction := input_dir*speed
	var speed_increase := delta * accel
	velocity.x = move_toward(velocity.x,direction.x ,speed_increase)
	velocity.z = move_toward(velocity.z,direction.z ,speed_increase)

	if not is_on_floor():
		velocity += get_gravity() * delta
		$AnimationTree.locomotion_state_jump=false
	#wall run
	if is_on_wall_only():
		if velocity.y<0:
			velocity -= get_wall_normal()*wall_run_stickyness
			velocity.y*=wall_friction
		# wall run up
		velocity.y-= get_wall_normal().dot(direction.normalized())*wall_accel*delta

	# stamina usage
	if input_dir:
		var v := velocity
		v.y = max(0,v.y)*3
		$Stats/Stamina.value -= v.length()*delta*3

	move_and_slide()

@export_category("Jump")
@export var jump := 4.5
@export var wall_jump := 4.4
@export var air_jump := 2.0
@export var jump_stamina_cost := 1.0
@export var wall_jump_stamina_cost := 1.2
@export var air_jump_stamina_cost := 1.5
var jump_bump := 0.0

func process_jump():
	if $Stats/Stamina.value == 0: return
	$AnimationTree.locomotion_state_jump = true
	jump_bump = jump if is_on_floor() else wall_jump if is_on_wall() else air_jump
	velocity.y += jump_bump
	jump_bump *=jump_stamina_cost if is_on_floor() else wall_jump_stamina_cost if is_on_wall() else air_jump_stamina_cost
	$Stats/Stamina.value -= jump_bump

func release_jump():
	if velocity.y > 0:
		velocity.y/=2
		# refund part of the stamina used in the jump
		$Stats/Stamina.value += jump_bump-velocity.y
		jump_bump = 0.0
#player.gd
class_name Player
extends Human



func _process(delta: float) -> void:
	if $Stats/Stamina.value == 0.0:release_jump()
	elif Input.is_action_just_pressed("ui_accept"): process_jump()

	var in_dir =  Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down").normalized()
	var dir = $Cam.global_basis *Vector3(in_dir.x, 0,in_dir.y)
	process_movement(Vector2(dir.x,dir.z),delta)

func _input(event: InputEvent) -> void:
	if event.is_action_released(&"ui_accept"):release_jump()

In other words player.gd has a _process() where Human.process_movement is called where move_and_slide() is called, so it should be called consistently

I dislike how loosely typed AnimationTree is and so I wrote those setters/getters to have better autocomplete and warnings
Also this is mostly a testing project where I try approaches, I don’t even intend to make a final game out of it

Something’s definitely not right here :).

Still the same error even after removing all references to $AnimationTree from human.gd and leaving only animation_tree.gd to control itself

This eventually fixes my problems:

extends AnimationTree

@onready var locomotion_state_playback :AnimationNodeStateMachinePlayback = self["parameters/LocomotionState/playback"]
var locomotion_state_jump:bool:
	get():return self["parameters/LocomotionState/conditions/jump"]
	set(v):
		self["parameters/LocomotionState/conditions/jump"]=v
		self["parameters/LocomotionState/conditions/land"]=!v

var locomotion_state_on_land_blend:float:
	get():return self["parameters/LocomotionState/On_Land/blend_position"]
	set(v):self["parameters/LocomotionState/On_Land/blend_position"]=v
@onready var human:Human=$".."
func _ready() -> void:
	locomotion_state_jump = false
func _physics_process(delta: float) -> void:
	locomotion_state_jump = not $"..".is_on_floor()
	locomotion_state_on_land_blend = $"..".velocity.length()/10

I’ve also added a transition from jump end to jump start directly so the jump animation can start immediately


Still when the game starts the animation is playing the falling position instead of the idle one

What I meant by ‘not right’ was the logic: both not is_on_floor() and is_on_floor() were switching the jump state to false. And I still think the problem is in logic, not in the animation tree setup.
Or, again, it’s the is_on_floor() function behaviour, it is an internal physics function, so it could produce unexpected results - I suggest you test it thoroughly, for example in a simplest way print(...) its returns and see if it behaves like you want it to.

It’s a bit hard to analyze such problems without being immersed in the project, so I could be wrong in my assumptions.

Again the value now result always false, as intended