Unable to interpolate blend2_amount over duration of time with code (Using AnimationTree)

Godot Version

v4.4.1.stable.official [49a5bc7b6]

Question

Hi, I’m new to the forums, thanks in advance to anyone checking this post out!

I’m a beginner to Godot and I started setting up a 3D character. I’m using the AnimationTree feature, so far I’ve got a character walking, jumping and punching with controller & mouse input. I’m using ‘Enable filtering’ to separate legs from upper body animation – the punches are controlled in the transition and the walking is in the statemachine.

THE PROBLEM:

is with the blending of the punch animation and controlling the blend amount with some time, smoothing or duration of some kind. The problem is in

animation_tree.set(“parameters/Blend2/blend_amount”, value_of_blend2towards)

I’m unable to interpolate the blend_amount smoothly.

The Blend2’s blend_amount is accessible in the inspector and easy to change the sliders value manually. I’ve also managed to access it through code but I’m unable to wrap my head around how to make the blend_amount interpolate from 1 back to zero using some kind of duration based in time or frames, or a delay, countdown or something to that effect.

Now, anytime the character punches, the upperbody freezes in the final/first frame of the punch loop and never interpolates back to the idle/walking.

I’m trying to achieve a smooth wind down where shortly after the punch concluded it starts blending/resetting back to zero inside the Blend2’s blend_amount.

I have tried various ways to make it interpolate without success

There’s two scripts associated to the character;

one holds the control inputs including physics_process (terrible_monster_fbx_to_glb_all_anims.gd)

and the other the animations of the character (player_3d.gd.)

Any help is greatly appreciated.

Here’s the code (problem is in func punch(delta):

extends Node3D

var t = 0.0

var frames_between_trigger: int = 120
var frame_counter: int = 0

@onready var var_special2 = get_parent().reference_speed

@onready var animation_tree: AnimationTree = $AnimationTree
#(my addition first row below)
@onready var animation_player: AnimationPlayer = $AnimationPlayer

@onready var state_machine : AnimationNodeStateMachinePlayback = animation_tree.get("parameters/StateMachine/playback")
@onready var transition_thingy : AnimationNodeTransition = animation_tree.get("parameters/Transition")

#@onready var state_machine : AnimationTree = animation_tree.get("parameters/StateMachine/playback")
@onready var move_tilt_path : String = "parameters/StateMachine/Move/tilt/add_amount"


@onready var _mesh: GeometryInstance3D = %Terrible_monster_fbx_to_glb_test3/Character1_Reference/Skeleton3D/polySurface71
@onready var _mesh2: GeometryInstance3D = %Terrible_monster_fbx_to_glb_test3/Character1_Ctrl_Reference/Character1_Ctrl_Hips/MAIN_Controll_TAIL_nurb/Armature/Skeleton3D/TAIL


var run_tilt = 0.0 : set = _set_run_tilt

func _ready():
	
	get_parent_node_3d()

func _set_run_tilt(value : float):
	run_tilt = clamp(value, -1.0, 1.0)
	animation_tree.set(move_tilt_path, run_tilt)
	
func idle():
	state_machine.travel("Idle")
	animation_tree.set("parameters/TimeScale/scale", 0.5)

	var_special2 = 1.0 if var_special2 > 1.0 else var_special2
	print("var_special2 first", var_special2)
func move():
	state_machine.travel("Move")
	animation_tree.set("parameters/TimeScale/scale", abs(var_special2))
	print("var_special2 ", var_special2)

func land():
	state_machine.travel("Land")

func jump():
	state_machine.travel("Jump")
	
func punch(delta):
	#TRY move_toward() for interpolation from one value to another with delta
	t += delta * 0.4
	frame_counter += 1
	#var x : float = lerp(animation_tree.get("parameters/Blend2/blend_amount"), 1.0, 0.1 * delta)
	
	var value_of_blend = 0.0
	value_of_blend = 1.0#delta * 1.0#Interpolation = A+(B-A) * t 0.0(1.0-0.0)*40.0
	var value_of_blend2towards = move_toward(0.0, 1.0, 1.0)#delta)
	print("Did we PUNCH??")
	animation_tree.set("parameters/Blend2/blend_amount", value_of_blend2towards) #THE BASE ONE!!!! 1 1 1 
	#print(x)
	#animation_tree.set("parameters/Blend2/blend_amount", float(x))
	#animation_tree.set("parameters/Transition/transition_request", "state_2")
	animation_tree.set("parameters/Transition/transition_request", "state_0")
	#animation_tree.set("parameters/Blend2/blend_amount", 0.0)
	if frame_counter >= frames_between_trigger:
		animation_tree.set("parameters/Transition/transition_request", "state_1")
		frame_counter = 0
		print("Counter reached ")

func _on_player_3d_reference_speed_2(value: Variant) -> void:
	var_special2 = value
	pass # Replace with function body.

This will always be 1.0 so it won’t blend anything.

AnimationNodeBlend2 does not reset its input animations, it only blends them. If one of the inputs isn’t looping, like the ones in the blend input, the last frame of that animation will be blended. If you are transitioning to the same state in your AnimationNodeTransition you’ll need to enable AnimationNodeTranstion.transition_to_self and set the input reset property to true This will reset the animation every time you transition to it so if you are doing it each frame only the first frame of the animation will show.

1 Like

Thank you so much for the reply!

I see, So AnimationNodeBlend2 does not reset, it only blends the inputted animations.

Then my solution would be to just bring the blending back to zero.

How do I make the move_toward value reach zero in a gradual way, fade out(?) after a certain time or amount of frames have elapsed(?). Hope this makes sense.

I hope the code appears correctly formatted, this is my first time pasting code online in a forum, hence why I also made some screenshots to accompany it just in case.

I see! If I understand it better now,
the third value in move_toward is how much it grows by delta?

I tried to insert a second snippet of code:
value_of_blend2towards = move_toward(1.0, 0.0, 0.5)
where it blends to zero but now it seem to only go half way, it doesn’t fully reach zero and there doesn’t seem to be any gradual transition/interpolation between the values,
it’s as if it jumps straight to the second value.

punch(delta):
#TRY move_toward() for interpolation from one value to another with delta
t += delta * 0.4
frame_counter += 1

var value_of_blend = 0.0
value_of_blend = 1.0#delta * 1.0#Interpolation = A+(B-A) * t 0.0(1.0-0.0)*40.0

#move towards values (FROM start value) (TO value) (By how much multiply delta time)
var value_of_blend2towards = move_toward(0.0, 1.0, 1.0)#delta)
print("Did we PUNCH??")
animation_tree.set("parameters/Blend2/blend_amount", value_of_blend2towards)

animation_tree.set("parameters/Transition/transition_request", "state_0")

value_of_blend2towards = move_toward(1.0, 0.0, 0.5)
animation_tree.set("parameters/Blend2/blend_amount", value_of_blend2towards)

if frame_counter >= frames_between_trigger:
	animation_tree.set("parameters/Transition/transition_request", "state_1")
	frame_counter = 0
	print("Counter reached ")

Here’s the documentation about @GlobalScope.move_toward()

The from parameter needs to be the current value not the absolute value:

var blend_value = 0.0
var target_value = 1.0

func _process(delta: float) -> void:
    blend_value = move_toward(blend_value, target_value, delta)
1 Like

Thank you so much for the reply and for the example code!

I see!

So I need to put it inside a process function for it to start incrementing to the target_value?