[Journal/Samples] How to setup AnimationTree by code

These are tested personal samples/notes in-progress that may evolve into examples or a demonstration for general use.

Here we create AnimationTree under male_base_mesh, assign BlendTree as tree_root, then we setup animations from AnimationPlayer and connect one of the animations to the output node. All programatically.

male_base_mesh is a basic Node3D (.glb scene)

func _ready() -> void:
	var anim_tree = AnimationTree.new()
	
	$male_base_mesh.add_child(anim_tree)
	
	# Creating BlendTree as a AnimationTree Root Node
	# Attaching animation player to the AnimationTree.
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	# Creating simple animation nodes and 
	# linking animations from AnimationPlayer
	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	# Connecting one of the animation nodes to the output node.
	tree_root.connect_node("output", 0, "Walking")

An AnimationNodeOutput node named output is created by default.
AnimationNodeBlendTree - Godot Documentation.


How to add a Blend2 node with a node name “Blend2”:

	tree_root.add_node("Blend2", AnimationNodeBlend2.new())

How to resolve AnimationTree having dynamic node name:
image

	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"

Version 2: Adding Blend2 and AnimationTree node naming

After applying the previous newfound ideas we will have something like this:

func _ready() -> void:
	var anim_tree = AnimationTree.new()
	
	$male_base_mesh.add_child(anim_tree)
	
	# Creating BlendTree as a AnimationTree Root Node
	# Attaching animation player to the AnimationTree.
	var tree_root = AnimationNodeBlendTree.new()

	anim_tree.name = "AnimationTree"
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	# How to add a Blend2 node with a node name “Blend2”:
	tree_root.add_node("Blend2", AnimationNodeBlend2.new())

	# Creating simple animation nodes and 
	# linking animations from AnimationPlayer
	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	# Connecting one of the animation nodes to the output node.
	tree_root.connect_node("output", 0, "Walking")

This is how it may look at runtime, I don’t think Godot AnimationTree UI have support for runtime, so this is only my proven sketch of what it looks like:

Now we have very basic AnimationTree that is already working:
If you run the project scene, the “Walking” animation will play once into the output.

Version 3: Blend2 node setup

In this version we will start to use Blend2 and begin a real world application of blending
animations. The previous versions were more of a basics demonstration.


Blend2 General

How to set blend_amount for blend2 node:
anim_tree.set("parameters/Blend2/blend_amount", 0.5)

How to connect idle animation as input of Blend2 and then blend Walking:

	tree_root.connect_node("Blend2", 0, "idle")
	tree_root.connect_node("Blend2", 1, "Walking")
	tree_root.connect_node("output", 0, "Blend2")

Blend2 Bone filtering

How to enable bone filtering for blend2 node:
tree_root.get_node("Blend2").filter_enabled = true

How to activate filter for a bone or bones.

	# ---- FILTER SPECIFIC BONES ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2").set_filter_path("metarig/Skeleton3D:" + bone, true)
		

Bone filtering debug/test/demo script that applies bone filter to each bone every second.
func _ready() -> void:
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim)
		anim.animation = anim_name

	tree_root.add_node("Blend2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2").filter_enabled = true
	anim_tree.set("parameters/Blend2/blend_amount", 0.8)
	tree_root.connect_node("Blend2", 0, "mining")
	tree_root.connect_node("Blend2", 1, "Walking")
	tree_root.connect_node("output", 0, "Blend2")
	anim_tree.active = true

	# Collect all bone names from the skeleton
	var skeleton := $male_base_mesh/metarig/Skeleton3D
	if skeleton is Skeleton3D:
		for i in range(skeleton.get_bone_count()):
			bone_names.append(skeleton.get_bone_name(i))

		# Set up the timer
		var timer = Timer.new()
		timer.wait_time = 1.0
		timer.one_shot = false
		timer.autostart = true
		timer.name = "BoneDebugTimer"
		add_child(timer)
		timer.timeout.connect(_on_bone_debug_tick.bind(tree_root))

func _on_bone_debug_tick(tree_root: AnimationNodeBlendTree) -> void:
	if bone_index >= bone_names.size():
		$BoneDebugTimer.stop()
		print("Bone filtering test finished.")
		return

	var bone_name = bone_names[bone_index]
	bone_index += 1

	var path = NodePath("metarig/Skeleton3D:" + bone_name)
	tree_root.get_node("Blend2").set_filter_path(path, true)
	print("Filtered bone:", bone_name)
Bone filtering debug/test/demo script, recursive bone filtering.
func _ready() -> void:
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.add_node("Blend2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2").filter_enabled = true
	anim_tree.set("parameters/Blend2/blend_amount", 1)

	tree_root.connect_node("Blend2", 0, "idle")
	tree_root.connect_node("Blend2", 1, "Walking")
	tree_root.connect_node("output", 0, "Blend2")
	anim_tree.active = true

	# ---- FILTER SPECIFIC BONES ----
	var skeleton := $male_base_mesh/metarig/Skeleton3D
	var blend_node := tree_root.get_node("Blend2")

	var left_leg_paths = get_bone_and_children_paths(skeleton, "thigh.L")
	for path in left_leg_paths:
		blend_node.set_filter_path(path, true)

	var right_leg_paths = get_bone_and_children_paths(skeleton, "thigh.R")
	for path in right_leg_paths:
		blend_node.set_filter_path(path, true)

	print("Applied filtering to left and right leg bones.")

# Helper to collect a bone and all its children as NodePaths
func get_bone_and_children_paths(skeleton: Skeleton3D, bone_name: String, base_path: String = "metarig/Skeleton3D") -> Array:
	var result: Array = []
	var bone_index := skeleton.find_bone(bone_name)
	if bone_index == -1:
		push_warning("Bone not found: " + bone_name)
		return result

	_collect_bone_children(skeleton, bone_index, base_path, result)
	return result

# Recursive function to gather all child bones
func _collect_bone_children(skeleton: Skeleton3D, index: int, base_path: String, result: Array) -> void:
	var name = skeleton.get_bone_name(index)
	result.append(NodePath(base_path + ":" + name))
	for child in skeleton.get_bone_children(index):
		_collect_bone_children(skeleton, child, base_path, result)

Bone filtering debug/test/demo script, recursive bone lister
func _ready() -> void:
	var skeleton := $male_base_mesh/metarig/Skeleton3D
	var bone_name := "shoulder.R"
	var result := get_bone_and_children_names(skeleton, bone_name)
	print(result)

func get_bone_and_children_names(skeleton: Skeleton3D, bone_name: String) -> Array:
	var result: Array = []
	var bone_index := skeleton.find_bone(bone_name)
	if bone_index == -1:
		push_warning("Bone not found: " + bone_name)
		return result

	_collect_bone_names(skeleton, bone_index, result)
	return result

func _collect_bone_names(skeleton: Skeleton3D, index: int, result: Array) -> void:
	var name := skeleton.get_bone_name(index)
	result.append(name)
	for child in skeleton.get_bone_children(index):
		_collect_bone_names(skeleton, child, result)
		

Blend2 Testing Misc

How to enable use_custom_timeline for animation node and enable animation node linear loop:

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 


Note: if animation is not cycling smoothly, stretch_time_scale may be useful to enable, if it’s not enabled by default. In my sample it was not needed to be enabled or is enabled by default.

Blend2 Setup sample

Here is entire sample that incorporates all the previous foundings to achieve blending between idle animation and Walking animation nodes.

func _ready() -> void:
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	
	tree_root.add_node("Blend2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2").filter_enabled = true
	anim_tree.set("parameters/Blend2/blend_amount", 1)

	tree_root.connect_node("Blend2", 0, "idle")
	tree_root.connect_node("Blend2", 1, "Walking")
	tree_root.connect_node("output", 0, "Blend2")

	# ---- FILTER SPECIFIC BONES ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2").set_filter_path("metarig/Skeleton3D:" + bone, true)

Result:
A minor upperbody swaying (idle) + legs movements (Walking)
2025-05-1409-24-12-ezgif.com-cut

Behind scenes:
2025-05-1409-40-55-ezgif.com-video-to-gif-converter

Blend2 Chained Setup sample

Now we can go on and introduce more Blend2 nodes and connect them.
This way the idle, the Walking and mining animations all blended.

func _ready() -> void:
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	#Walking animation was exported with loop -cycle suffix https://docs.godotengine.org/en/4.3/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html#animation-loop-loop-cycle
	#tree_root.get_node("Walking").use_custom_timeline = true
	#tree_root.get_node("Walking").loop_mode = 1 
	tree_root.get_node("mining").use_custom_timeline = true
	tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")


	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 1)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	tree_root.connect_node("Blend2-2", 1, "mining")
	tree_root.connect_node("output", 0, "Blend2-2")	
		
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)
		

		

2025-05-1412-04-08-ezgif.com-video-to-gif-converter


4 Likes

… In progress and research

Triggering Blend2 animations and blending animations on input

Now the final idea is use blend_amount variable to toggle between the animations:
While Walking and idle are active, only mouse should trigger the mining animation. (hands animation) Reseting of animation might be achieved by setting TimeSeek Node to 0 of AnimationTree.

	tree_root.add_node("TimeSeekBeforeBlend2-2", AnimationNodeTimeSeek.new())
	tree_root.connect_node("TimeSeekBeforeBlend2-2", 0, "mining")
	tree_root.connect_node("Blend2-2", 1, "TimeSeekBeforeBlend2-2")
	#anim_tree.set("parameters/TimeSeekBeforeBlend2-2/seek_request", 0)
	tree_root.connect_node("output", 0, "Blend2-2")
... in progress. Researching
extends CharacterBody3D

const SPEED: float = 5.0
const JUMP_VELOCITY: float = 4.5

var anim_tree: AnimationTree
var tree_root: AnimationNodeBlendTree
var mouse_pressed_last_frame: bool = false

func _ready() -> void:
	# --- Create and hook up AnimationTree + BlendTree root ---
	anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)

	tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	# --- Add base animation nodes ---
	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim_node: AnimationNodeAnimation = AnimationNodeAnimation.new()
		anim_node.animation = anim_name
		anim_node.use_custom_timeline = true
		anim_node.loop_mode = 1  # Loop forward
		tree_root.add_node(anim_name, anim_node)

	# --- First Blend2: idle ↔ Walking (legs) ---
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	var b2_1 = tree_root.get_node("Blend2-1") as AnimationNodeBlend2
	b2_1.filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1.0)
	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")
	for bone in ["thigh.L","shin.L","foot.L","toe.L","thigh.R","shin.R","foot.R","toe.R"]:
		b2_1.set_filter_path("metarig/Skeleton3D:" + bone, true)

	# --- Second Blend2: (legs mix) ↔ mining (arms/hands) ---
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	var b2_2 = tree_root.get_node("Blend2-2") as AnimationNodeBlend2
	b2_2.filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 0.0)
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	tree_root.connect_node("Blend2-2", 1, "mining")
	tree_root.connect_node("output", 0, "Blend2-2")
	for bone in [
			"shoulder.L","upper_arm.L","forearm.L","hand.L",
			"f_index.01.L","f_index.02.L","f_index.03.L",
			"thumb.01.L","thumb.02.L","thumb.03.L","f_middle.01.L",
			"shoulder.R","upper_arm.R","forearm.R","hand.R",
			"f_index.01.R","f_index.02.R","f_index.03.R",
			"thumb.01.R","thumb.02.R","thumb.03.R",
			"f_middle.01.R","f_middle.02.R","f_middle.03.R",
			"f_ring.01.R","f_ring.02.R","f_ring.03.R",
			"f_pinky.01.R","f_pinky.02.R","f_pinky.03.R"
		]:
		b2_2.set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	# --- Detect click ---
	var mouse_pressed: bool = Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)
	var just_clicked: bool = mouse_pressed and not mouse_pressed_last_frame

	# --- On click: recreate the mining node to reset it ---
	if just_clicked:
		# 1. Remove old mining node
		tree_root.remove_node("mining")
		# 2. Create & configure new one
		var new_mining: AnimationNodeAnimation = AnimationNodeAnimation.new()
		
		new_mining.animation = "mining"
		new_mining.use_custom_timeline = true
		new_mining.loop_mode = 1
		# 3. Add back and reconnect into Blend2-2
		tree_root.add_node("mining", new_mining)
		tree_root.connect_node("Blend2-2", 1, "mining")

	# --- Blend mining in/out smoothly while the button is held ---
	var target_blend: float = 1.0 if mouse_pressed else 0.0
	var current_blend: float = float(anim_tree.get("parameters/Blend2-2/blend_amount"))
	var new_blend: float = lerp(current_blend, target_blend, delta * 10.0)
	anim_tree.set("parameters/Blend2-2/blend_amount", new_blend)

	mouse_pressed_last_frame = mouse_pressed

	# --- Standard movement & physics below ---
	if not is_on_floor():
		velocity += get_gravity() * delta

	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()

Left mouse button handling and TimeSeek for resetting animation.
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

var anim_tree: AnimationTree

func _ready() -> void:
	anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	tree_root.get_node("mining").use_custom_timeline = true
	tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")

	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 0)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	tree_root.add_node("TimeSeekBeforeBlend2-2", AnimationNodeTimeSeek.new())
	tree_root.connect_node("TimeSeekBeforeBlend2-2", 0, "mining")
	tree_root.connect_node("Blend2-2", 1, "TimeSeekBeforeBlend2-2")
	tree_root.connect_node("output", 0, "Blend2-2")	
	
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()

	# Restart mining animation when left mouse is clicked
var anim_playing = false

func _input(event):
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return  # Ignore if already playing
		anim_playing = true

		print("Left mouse button pressed at:", event.position)
		anim_tree.set("parameters/TimeSeekBeforeBlend2-2/seek_request", 0.0)
		anim_tree.set("parameters/Blend2-2/blend_amount", 1)

		await get_tree().create_timer(1.0).timeout
		anim_tree.set("parameters/Blend2-2/blend_amount", 0)
		anim_playing = false  # Unlock input

Allowing to play full mining animation
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

var anim_tree: AnimationTree
var anim_playing = false

func _ready() -> void:
	anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	tree_root.get_node("mining").use_custom_timeline = true
	tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")

	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 0)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	tree_root.add_node("TimeSeekBeforeBlend2-2", AnimationNodeTimeSeek.new())
	tree_root.connect_node("TimeSeekBeforeBlend2-2", 0, "mining")
	tree_root.connect_node("Blend2-2", 1, "TimeSeekBeforeBlend2-2")
	tree_root.connect_node("output", 0, "Blend2-2")	
	
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity += get_gravity() * delta

	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()

func _input(event):
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return
		anim_playing = true

		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining
		anim_tree.set("parameters/TimeSeekBeforeBlend2-2/seek_request", 0.0)
		anim_tree.set("parameters/Blend2-2/blend_amount", 1)

		# Get mining animation duration
		var anim_player = $male_base_mesh/AnimationPlayer
		var anim_name = "mining"
		var anim_length = anim_player.get_animation(anim_name).length

		# Wait until animation finishes
		await get_tree().create_timer(anim_length).timeout

		# Blend out mining animation
		anim_tree.set("parameters/Blend2-2/blend_amount", 0)
		anim_playing = false

…in-progress… The main issue right now is starting pose is idle animation and not mining animation. It seems like TimeScale node allows to pause or play animation. Might be a solution.

Working code needs a refinement. In-progress.
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

var anim_tree: AnimationTree
var anim_playing = false

func _ready() -> void:
	anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	tree_root.get_node("mining").use_custom_timeline = true
	tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")

	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 1)

	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	# update: Timescale node
	tree_root.add_node("TimeSeekBeforeBlend2-2", AnimationNodeTimeSeek.new())
	tree_root.connect_node("TimeSeekBeforeBlend2-2", 0, "mining")
	
	tree_root.add_node("TimeScaleBeforeBlend2-2", AnimationNodeTimeScale.new())
	tree_root.connect_node("TimeScaleBeforeBlend2-2", 0, "TimeSeekBeforeBlend2-2")
	tree_root.connect_node("Blend2-2", 1, "TimeScaleBeforeBlend2-2")
	anim_tree.set("parameters/TimeScaleBeforeBlend2-2/scale", 0.0)
	anim_tree.set("parameters/TimeSeekBeforeBlend2-2/seek_request", 0.0)
	
	# update: Timescale node
	
	tree_root.connect_node("output", 0, "Blend2-2")	
	
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity += get_gravity() * delta

	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()
	


func _input(event):
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return
		anim_playing = true

		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining

		#anim_tree.set("parameters/Blend2-2/blend_amount", 1)
		anim_tree.set("parameters/TimeScaleBeforeBlend2-2/scale", 0.0)
		anim_tree.set("parameters/TimeSeekBeforeBlend2-2/seek_request", 0.0)
		anim_tree.set("parameters/TimeScaleBeforeBlend2-2/scale", 1.0)
		
		# Get mining animation duration
		var anim_player = $male_base_mesh/AnimationPlayer
		var anim_name = "mining"
		var anim_length = anim_player.get_animation(anim_name).length

		# Wait until animation finishes
		await get_tree().create_timer(anim_length).timeout

		# Blend out mining animation
		
		anim_tree.set("parameters/TimeScaleBeforeBlend2-2/scale", 0.0)
		anim_tree.set("parameters/TimeSeekBeforeBlend2-2/seek_request", 0.13)
		#anim_tree.set("parameters/Blend2-2/blend_amount", 0)
		anim_playing = false


Pausing animation making, pose before playing the animation.

	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 1)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	tree_root.add_node("TimeSeekBlend2-2", AnimationNodeTimeSeek.new())
	tree_root.connect_node("TimeSeekBlend2-2", 0, "mining")
	anim_tree.set("parameters/TimeSeekBlend2-2/seek_request", 0.0)
	
	tree_root.add_node("TimeScaleBlend2-2", AnimationNodeTimeScale.new())
	tree_root.connect_node("TimeScaleBlend2-2", 0, "TimeSeekBlend2-2")
	tree_root.connect_node("Blend2-2", 1, "TimeScaleBlend2-2")
	anim_tree.set("parameters/TimeScaleBlend2-2/scale", 0.0)

Controlling the animation on input such as mouse click.
Usage:

var anim_playing = false
func _input(event):
	var anim_tree_exter = $male_base_mesh/AnimationTree
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return
		anim_playing = true

		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining

		#anim_tree.set("parameters/Blend2-2/blend_amount", 1)
		anim_tree_exter.set("parameters/TimeScaleBlend2-2/scale", 0.0)
		anim_tree_exter.set("parameters/TimeSeekBlend2-2/seek_request", 0.0)
		anim_tree_exter.set("parameters/TimeScaleBlend2-2/scale", 1.0)
		
		# Get mining animation duration
		var anim_player = $male_base_mesh/AnimationPlayer
		var anim_name = "mining"
		var anim_length = anim_player.get_animation(anim_name).length

		# Wait until animation finishes
		await get_tree().create_timer(anim_length).timeout

		# Blend out mining animation
		
		anim_tree_exter.set("parameters/TimeScaleBlend2-2/scale", 0.0)
		anim_tree_exter.set("parameters/TimeSeekBlend2-2/seek_request", 0.13)
		#anim_tree.set("parameters/Blend2-2/blend_amount", 0)
		anim_playing = false


2025-05-1510-06-36-ezgif.com-video-to-gif-converter

Entire final example
extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5

func _ready() -> void:
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	#Walking animation was exported with loop -cycle suffix https://docs.godotengine.org/en/4.3/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html#animation-loop-loop-cycle
	#tree_root.get_node("Walking").use_custom_timeline = true
	#tree_root.get_node("Walking").loop_mode = 1 
	tree_root.get_node("mining").use_custom_timeline = true
	tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")


	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 1)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	tree_root.add_node("TimeSeekBlend2-2", AnimationNodeTimeSeek.new())
	tree_root.connect_node("TimeSeekBlend2-2", 0, "mining")
	anim_tree.set("parameters/TimeSeekBlend2-2/seek_request", 0.0)
	
	tree_root.add_node("TimeScaleBlend2-2", AnimationNodeTimeScale.new())
	tree_root.connect_node("TimeScaleBlend2-2", 0, "TimeSeekBlend2-2")
	tree_root.connect_node("Blend2-2", 1, "TimeScaleBlend2-2")
	anim_tree.set("parameters/TimeScaleBlend2-2/scale", 0.0)

	
	
	tree_root.connect_node("output", 0, "Blend2-2")	
		
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()


var anim_playing = false
func _input(event):
	var anim_tree_exter = $male_base_mesh/AnimationTree
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return
		anim_playing = true

		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining

		#anim_tree.set("parameters/Blend2-2/blend_amount", 1)
		anim_tree_exter.set("parameters/TimeScaleBlend2-2/scale", 0.0)
		anim_tree_exter.set("parameters/TimeSeekBlend2-2/seek_request", 0.0)
		anim_tree_exter.set("parameters/TimeScaleBlend2-2/scale", 1.0)
		
		# Get mining animation duration
		var anim_player = $male_base_mesh/AnimationPlayer
		var anim_name = "mining"
		var anim_length = anim_player.get_animation(anim_name).length

		# Wait until animation finishes
		await get_tree().create_timer(anim_length).timeout

		# Blend out mining animation
		
		anim_tree_exter.set("parameters/TimeScaleBlend2-2/scale", 0.0)
		anim_tree_exter.set("parameters/TimeSeekBlend2-2/seek_request", 0.13)
		#anim_tree.set("parameters/Blend2-2/blend_amount", 0)
		anim_playing = false

Maybe a more correct approach would be to use oneshot node to trigger mining animation and use Blend2 with TimeScale to keep the mining pose. Seems to work quite well.
However OneShot does not blend unless Fadein property is set to 1 second.
Fading affects the walking and idle animations, maybe setting bone filters would help.
Yes it seems like OneShot is the correct choice over Blend2 for actually triggering the animation.
Setting bone filters helped with the issue.Now we might have cleaner setup than all the previous.
However it still seems like the major downside is having two animation nodes of mining: one (Blend2) for posing and other (OneShot) for triggering the animation.
…Researching…

Timescale/Blend2 Pose holding and OneShot node for triggering animation.

extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5


func _ready() -> void:
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	#Walking animation was exported with loop -cycle suffix https://docs.godotengine.org/en/4.3/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html#animation-loop-loop-cycle
	#tree_root.get_node("Walking").use_custom_timeline = true
	#tree_root.get_node("Walking").loop_mode = 1 
	# OneShot does not require for mining to be on loop
	#tree_root.get_node("mining").use_custom_timeline = true
	#tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")


	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 1)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	tree_root.add_node("TimeScaleBlend2-2", AnimationNodeTimeScale.new())
	tree_root.connect_node("Blend2-2", 1, "TimeScaleBlend2-2")
	anim_tree.set("parameters/TimeScaleBlend2-2/scale", 0.0)
	
	var MiningPosingAnim = AnimationNodeAnimation.new()
	MiningPosingAnim.animation = "mining"
	tree_root.add_node("MiningPosing", MiningPosingAnim)
	tree_root.connect_node("TimeScaleBlend2-2", 0, "MiningPosing")
	
	
	tree_root.add_node("AnimationOneShot", AnimationNodeOneShot.new())
	tree_root.connect_node("AnimationOneShot", 0, "Blend2-2")	
	tree_root.connect_node("AnimationOneShot", 1, "mining")
	tree_root.get_node("AnimationOneShot").filter_enabled = true
	
	tree_root.connect_node("output", 0, "AnimationOneShot")	
	# ---- FILTER SPECIFIC BONES AnimationOneShot ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("AnimationOneShot").set_filter_path("metarig/Skeleton3D:" + bone, true)

	
		
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()



func _input(event):
	var anim_tree_exter = $male_base_mesh/AnimationTree
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:


		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining

		#anim_tree.set("parameters/Blend2-2/blend_amount", 1)
		anim_tree_exter.set("parameters/AnimationOneShot/request", true)

Waiting until animation is over, on input/mouse press.

var anim_playing = false
func _input(event):
	var anim_tree_exter = $male_base_mesh/AnimationTree
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return
		anim_playing = true
		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining

		#anim_tree.set("parameters/Blend2-2/blend_amount", 1)
		anim_tree_exter.set("parameters/AnimationOneShot/request", true)
		
		# Get mining animation duration
		var anim_player = $male_base_mesh/AnimationPlayer
		var anim_name = "mining"
		var anim_length = anim_player.get_animation(anim_name).length

		# Wait until animation finishes
		await get_tree().create_timer(anim_length).timeout
		anim_playing = false

2025-05-1520-18-49-ezgif.com-video-to-gif-converter

Note:

   	tree_root.get_node("Blend2-2").set_filter_path("Pickaxe:" + "", true)
   	tree_root.get_node("AnimationOneShot").set_filter_path("Pickaxe:" + "", true)

func _ready() -> void:
	var pickaxe2 = $Pickaxe2
	var target_parent = $male_base_mesh/Pickaxe

	# reparent pickaxe
	pickaxe2.get_parent().remove_child(pickaxe2)
	target_parent.add_child(pickaxe2)
	

2025-05-1523-11-52-ezgif.com-cut

Full code:
extends CharacterBody3D


const SPEED = 5.0
const JUMP_VELOCITY = 4.5

 


func _ready() -> void:
	var pickaxe2 = $Pickaxe2
	var target_parent = $male_base_mesh/Pickaxe

	# Remove from current parent and add to new parent
	pickaxe2.get_parent().remove_child(pickaxe2)
	target_parent.add_child(pickaxe2)
	
	var anim_tree = AnimationTree.new()
	anim_tree.name = "AnimationTree"
	$male_base_mesh.add_child(anim_tree)
	
	var tree_root = AnimationNodeBlendTree.new()
	anim_tree.tree_root = tree_root
	anim_tree.anim_player = $male_base_mesh/AnimationPlayer.get_path()

	var animations = ["idle", "Walking", "mining"]
	for anim_name in animations:
		var anim = AnimationNodeAnimation.new()
		tree_root.add_node(anim_name, anim) 
		anim.animation = anim_name

	tree_root.get_node("idle").use_custom_timeline = true
	tree_root.get_node("idle").loop_mode = 1 
	#Walking animation was exported with loop -cycle suffix https://docs.godotengine.org/en/4.3/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html#animation-loop-loop-cycle
	#tree_root.get_node("Walking").use_custom_timeline = true
	#tree_root.get_node("Walking").loop_mode = 1 
	# OneShot does not require for mining to be on loop
	#tree_root.get_node("mining").use_custom_timeline = true
	#tree_root.get_node("mining").loop_mode = 1 
	
	# ---- First Blend2 node ----
	tree_root.add_node("Blend2-1", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-1").filter_enabled = true
	anim_tree.set("parameters/Blend2-1/blend_amount", 1)

	tree_root.connect_node("Blend2-1", 0, "idle")
	tree_root.connect_node("Blend2-1", 1, "Walking")


	# ---- FILTER SPECIFIC BONES Blend2-1 ----
	var filter_bones = [
		"thigh.L", "shin.L", "foot.L", "toe.L",
		"thigh.R", "shin.R", "foot.R", "toe.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-1").set_filter_path("metarig/Skeleton3D:" + bone, true)
		
	# ---- Second Blend2 node ----
	tree_root.add_node("Blend2-2", AnimationNodeBlend2.new())
	tree_root.get_node("Blend2-2").filter_enabled = true
	anim_tree.set("parameters/Blend2-2/blend_amount", 1)
	
	tree_root.connect_node("Blend2-2", 0, "Blend2-1")
	
	tree_root.add_node("TimeScaleBlend2-2", AnimationNodeTimeScale.new())
	tree_root.connect_node("Blend2-2", 1, "TimeScaleBlend2-2")
	anim_tree.set("parameters/TimeScaleBlend2-2/scale", 0.0)
	
	var MiningPosingAnim = AnimationNodeAnimation.new()
	MiningPosingAnim.animation = "mining"
	tree_root.add_node("MiningPosing", MiningPosingAnim)
	tree_root.connect_node("TimeScaleBlend2-2", 0, "MiningPosing")
	

	


	
	tree_root.add_node("AnimationOneShot", AnimationNodeOneShot.new())
	tree_root.connect_node("AnimationOneShot", 0, "Blend2-2")	
	tree_root.connect_node("AnimationOneShot", 1, "mining")
	tree_root.get_node("AnimationOneShot").filter_enabled = true
	
	tree_root.connect_node("output", 0, "AnimationOneShot")	
	# ---- FILTER SPECIFIC BONES AnimationOneShot ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("AnimationOneShot").set_filter_path("metarig/Skeleton3D:" + bone, true)



		tree_root.get_node("Blend2-2").set_filter_path("Pickaxe:" + "", true)
		tree_root.get_node("AnimationOneShot").set_filter_path("Pickaxe:" + "", true)

		
	# ---- FILTER SPECIFIC BONES Blend2-2 ----
	filter_bones = [
		"shoulder.L", "upper_arm.L", "forearm.L", "hand.L", "f_index.01.L", "f_index.02.L", "f_index.03.L", "thumb.01.L", "thumb.02.L", "thumb.03.L", "f_middle.01.L",
		"shoulder.R", "upper_arm.R", "forearm.R", "hand.R", "f_index.01.R", "f_index.02.R", "f_index.03.R", "thumb.01.R", "thumb.02.R", "thumb.03.R", "f_middle.01.R", "f_middle.02.R", "f_middle.03.R", "f_ring.01.R", "f_ring.02.R", "f_ring.03.R", "f_pinky.01.R", "f_pinky.02.R", "f_pinky.03.R"
	]
	for bone in filter_bones:
		tree_root.get_node("Blend2-2").set_filter_path("metarig/Skeleton3D:" + bone, true)

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()


var anim_playing = false
func _input(event):
	var anim_tree_exter = $male_base_mesh/AnimationTree
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if anim_playing:
			return
		anim_playing = true
		print("Left mouse button pressed at:", event.position)
		
		# Seek to start and blend in mining

		#anim_tree.set("parameters/Blend2-2/blend_amount", 1)
		anim_tree_exter.set("parameters/AnimationOneShot/request", true)
		
		# Get mining animation duration
		var anim_player = $male_base_mesh/AnimationPlayer
		var anim_name = "mining"
		var anim_length = anim_player.get_animation(anim_name).length

		# Wait until animation finishes
		await get_tree().create_timer(anim_length).timeout
		anim_playing = false