
Godot Animation Tree Mastery
- 214 installs
- 454 repo stars
- Updated July 28, 2026
- thedivergentai/gd-agentic-skills
Use godot-animation-tree-mastery for development tasks
About
godot-animation-tree-mastery: A skill for development. This provides functionality for development workflows.
- godot-animation-tree-mastery
Godot Animation Tree Mastery by the numbers
- 214 all-time installs (skills.sh)
- +12 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,833 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-animation-tree-masteryAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 214 |
|---|---|
| repo stars | ★ 454 |
| Last updated | July 28, 2026 |
| Repository | thedivergentai/gd-agentic-skills ↗ |
What it does
Use godot-animation-tree-mastery for development tasks
Files
AnimationTree Mastery
Expert guidance for Godot's advanced animation blending and state machines.
NEVER Do
- NEVER call `play()` on AnimationPlayer when using AnimationTree — AnimationTree controls the player. Directly calling
play()causes conflicts and jitter. Useset("parameters/transition_request")ortravel()instead [12]. - NEVER forget to set `active = true` — AnimationTree is inactive by default. Animations won't play until
$AnimationTree.active = true[13]. - NEVER use absolute paths for parameter access — Use relative paths like
"parameters/StateMachine/transition_request". This ensures compatibility when nodes move in the hierarchy [14]. - NEVER leave `auto_advance` enabled for interactive states — It causes immediate transitions. Use it only for automated sequences like combo chains or death-to-respawn [15, 121].
- NEVER use `BlendSpace2D` for 1D blending — Blending only speed? Use
BlendSpace1D. Blending only two states? UseBlend2.BlendSpace2Dis specifically for X+Y directional inputs (strafe) [16, 142]. - NEVER update `AnimationTree` parameters every frame without a guard — Setting parameters via
set()every frame regardless of change causes cache invalidation and potential stutter. Check equality first. - NEVER use deep, nested `BlendTrees` for simple logic — Every layer adds CPU overhead. If logic can be handled in a
StateMachineor a simple script-drivenBlend2, do it there. - NEVER forget to handle `await get_tree().process_frame` when updating parameters synchronously — Sometimes the tree needs one frame to reconcile state before the next parameter change takes effect.
- NEVER rely on `auto_advance` for long cutscenes — If an animation is interrupted,
auto_advancecan put the character in a broken state. UseMethod Tracksto signal state completion instead. - NEVER use `Sync` groups for animations with wildly different lengths — It forces one animation to play at an extreme speed. Use
TimeScaleor separate layers for mismatching cycles.
---
Available Scripts
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
sync_parameter_manager.gd
Expert management of AnimationTree parameters with guards to prevent redundant updates and GPU cache churn.
reactive_oneshot_vfx.gd
Using AnimationNodeOneShot for high-priority reactive animations like recoil, blinks, and hit reactions.
dynamic_timescale_control.gd
Runtime manipulation of playback speed for bullet-time effects or movement haste multipliers.
advanced_transition_masking.gd
Procedural bone filtering (masking) for nodes like Add2 to separate upper/lower body animations.
statemachine_travel_code.gd
Programmatic control of AnimationNodeStateMachinePlayback using travel() and start().
blendtree_logic_mixing.gd
Complex mixing patterns for BlendTree nodes to create interactive combat layers.
root_motion_animtree_sync.gd
Expert 3D CharacterBody motion extraction optimized specifically for AnimationTree nodes.
sync_group_layering.gd
Using Sync Groups to keep multi-layered animations (e.g. walk and reload) perfectly aligned.
nested_tree_architecture.gd
Pattern for managing hierarchical State Machines and nested node parameter paths.
runtime_tree_debugging.gd
Interactive tool for visualizing current states, transition paths, and blend values in real-time.
---
Core Concepts
AnimationTree Structure
AnimationTree (node)
├─ Root (assigned in editor)
│ ├─ StateMachine (common)
│ ├─ BlendTree (layering)
│ └─ BlendSpace (directional)
└─ anim_player: NodePath → points to AnimationPlayerParameter Access
# Set parameters using string paths
$AnimationTree.set("parameters/StateMachine/transition_request", "run")
$AnimationTree.set("parameters/Movement/blend_position", Vector2(1, 0))
# Get current state
var current_state = $AnimationTree.get("parameters/StateMachine/current_state")---
StateMachine Pattern
Basic Setup
# Scene structure:
# CharacterBody2D
# ├─ AnimationPlayer (has: idle, walk, run, jump, land)
# └─ AnimationTree
# └─ Root: AnimationNodeStateMachine
# StateMachine nodes (created in AnimationTree editor):
# - Idle (AnimationNode referencing "idle")
# - Walk (AnimationNode referencing "walk")
# - Run (AnimationNode referencing "run")
# - Jump (AnimationNode referencing "jump")
# - Land (AnimationNode referencing "land")
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var state_machine: AnimationNodeStateMachinePlayback = anim_tree.get("parameters/StateMachine/playback")
func _ready() -> void:
anim_tree.active = true
func _physics_process(delta: float) -> void:
var velocity := get_velocity()
# State transitions based on gameplay
if is_on_floor():
if velocity.length() < 10:
state_machine.travel("Idle")
elif velocity.length() < 200:
state_machine.travel("Walk")
else:
state_machine.travel("Run")
else:
if velocity.y < 0: # Rising
state_machine.travel("Jump")
else: # Falling
state_machine.travel("Land")Transition Conditions (Advance Expressions)
# In AnimationTree editor:
# Add transition from Idle → Walk
# Set "Advance Condition" to "is_walking"
# In code:
anim_tree.set("parameters/conditions/is_walking", true)
# Transition fires automatically when condition becomes true
# Useful for event-driven transitions (hurt, dead, etc.)
# Example: Damage transition
anim_tree.set("parameters/conditions/is_damaged", false) # Reset each frame
func take_damage() -> void:
anim_tree.set("parameters/conditions/is_damaged", true)
# Transition to "Hurt" state fires immediatelyAuto-Advance (Combo Chains)
# In AnimationTree editor:
# Transition from Attack1 → Attack2
# Enable "Auto Advance" (no condition needed)
# Code:
state_machine.travel("Attack1")
# Attack1 animation plays
# When Attack1 finishes, automatically transitions to Attack2
# When Attack2 finishes, transitions to Idle (next auto-advance)
# Useful for:
# - Attack combos
# - Death → Respawn
# - Cutscene sequences---
BlendSpace2D (Directional Movement)
8-Way Movement
# Create BlendSpace2D in AnimationTree editor:
# - Add animations at positions:
# - (0, -1): walk_up
# - (0, 1): walk_down
# - (-1, 0): walk_left
# - (1, 0): walk_right
# - (-1, -1): walk_up_left
# - (1, -1): walk_up_right
# - (-1, 1): walk_down_left
# - (1, 1): walk_down_right
# - (0, 0): idle (center)
# In code:
func _physics_process(delta: float) -> void:
var input := Input.get_vector("left", "right", "up", "down")
# Set blend position (AnimationTree interpolates between animations)
anim_tree.set("parameters/Movement/blend_position", input)
# BlendSpace2D automatically blends animations based on input
# input = (0.5, -0.5) → blends walk_right and walk_upBlendSpace1D (Speed Blending)
# For walk → run transitions
# Create BlendSpace1D:
# - Position 0.0: walk
# - Position 1.0: run
func _physics_process(delta: float) -> void:
var speed := velocity.length()
var max_speed := 400.0
var blend_value := clamp(speed / max_speed, 0.0, 1.0)
anim_tree.set("parameters/SpeedBlend/blend_position", blend_value)
# Smoothly blends from walk → run as speed increases---
BlendTree (Layered Animations)
Add Upper Body Animation
# Problem: Want to aim gun while walking
# Solution: Blend upper body (aim) with lower body (walk)
# In AnimationTree editor:
# Root → BlendTree
# ├─ Walk (lower body animation)
# ├─ Aim (upper body animation)
# └─ Add2 node (combines them)
# - Inputs: Walk, Aim
# - filter_enabled: true
# - Filters: Only enable upper body bones for Aim
# Code:
# No code needed! BlendTree auto-combines
# Just ensure animations are assignedBlend2 (Crossfade)
# Blend between two animations dynamically
# Root → BlendTree
# └─ Blend2
# ├─ Input A: idle
# └─ Input B: attack
# Code:
var blend_amount := 0.0
func _process(delta: float) -> void:
# Gradually blend from idle → attack
blend_amount += delta
blend_amount = clamp(blend_amount, 0.0, 1.0)
anim_tree.set("parameters/IdleAttackBlend/blend_amount", blend_amount)
# 0.0 = 100% idle
# 0.5 = 50% idle, 50% attack
# 1.0 = 100% attack---
Root Motion with AnimationTree
# Enable in AnimationTree
anim_tree.root_motion_track = NodePath("CharacterBody3D/Skeleton3D:Root")
func _physics_process(delta: float) -> void:
# Get root motion
var root_motion := anim_tree.get_root_motion_position()
# Apply to character (not velocity!)
global_position += root_motion.rotated(rotation.y)
# For CharacterBody3D with move_and_slide:
velocity = root_motion / delta
move_and_slide()---
Advanced Patterns
Sub-StateMachines
# Nested state machines for complex behavior
# Root → StateMachine
# ├─ Grounded (Sub-StateMachine)
# │ ├─ Idle
# │ ├─ Walk
# │ └─ Run
# └─ Airborne (Sub-StateMachine)
# ├─ Jump
# ├─ Fall
# └─ Glide
# Access nested states:
var sub_state = anim_tree.get("parameters/Grounded/playback")
sub_state.travel("Run")Time Scale (Slow Motion)
# Slow down specific animation without affecting others
anim_tree.set("parameters/TimeScale/scale", 0.5) # 50% speed
# Useful for:
# - Bullet time
# - Hurt/stun effects
# - Charge-up animationsSync Between Animations
# Problem: Switching from walk → run causes foot slide
# Solution: Use "Sync" on transition
# In AnimationTree editor:
# Transition: Walk → Run
# Enable "Sync" checkbox
# Godot automatically syncs animation playback positions
# Feet stay grounded during transition---
Debugging AnimationTree
Print Current State
func _process(delta: float) -> void:
var current_state = anim_tree.get("parameters/StateMachine/current_state")
print("Current state: ", current_state)
# Print blend position
var blend_pos = anim_tree.get("parameters/Movement/blend_position")
print("Blend position: ", blend_pos)Common Issues
# Issue: Animation not playing
# Solution:
if not anim_tree.active:
anim_tree.active = true
# Issue: Transition not working
# Check:
# 1. Is advance_condition set?
# 2. Is transition priority correct?
# 3. Is auto_advance enabled unintentionally?
# Issue: Blend not smooth
# Solution: Increase transition xfade_time (0.1 - 0.3s)---
Performance Optimization
Disable When Not Needed
# AnimationTree is expensive
# Disable for off-screen entities
extends VisibleOnScreenNotifier3D
func _ready() -> void:
screen_exited.connect(_on_screen_exited)
screen_entered.connect(_on_screen_entered)
func _on_screen_exited() -> void:
$AnimationTree.active = false
func _on_screen_entered() -> void:
$AnimationTree.active = true---
Decision Tree: When to Use AnimationTree
| Feature | AnimationPlayer Only | AnimationTree |
|---|---|---|
| Simple state swap | ✅ play("idle") | ❌ Overkill |
| Directional movement | ❌ Complex | ✅ BlendSpace2D |
| State machine (5+ states) | ❌ Messy code | ✅ StateMachine |
| Layered animations | ❌ Manual blending | ✅ BlendTree |
| Root motion | ✅ Possible | ✅ Built-in |
| Transition blending | ❌ Manual | ✅ Auto |
Use AnimationTree for: Complex characters with 5+ states, directional movement, layered animations Use AnimationPlayer for: Simple animations, UI, cutscenes, props
---
Expert Pattern: Animation-Event-Dispatcher
Decouple your animation frames from specific gameplay logic by using a generalized dispatcher that passes metadata (e.g., surface type for footsteps) through signals.
class_name AnimationEventDispatcher extends Node
signal animation_event(event_name: String, metadata: Variant)
## Generic function called by AnimationPlayer Method Tracks
func dispatch_event(event_name: String, metadata: Variant) -> void:
animation_event.emit(event_name, metadata)
# Workflow:
# 1. Add Method Track to animation (e.g., "walk")
# 2. Keyframe: method="dispatch_event", args=["footstep", "stone"]
# 3. Audio manager listens to signal and plays correct 'stone' SFX.---
Expert Pattern: Procedural-In-Place-Rotation
Use a BlendTree to procedurally blend turning animations based on rotation input, providing more natural stationary turns than simple state changes.
# Root -> BlendTree
# └─ TurnBlend (AnimationNodeBlend2)
# ├─ Input 0: Idle
# └─ Input 1: TurnRight
func _physics_process(delta: float) -> void:
var turn_input := Input.get_axis("left", "right")
# 0.0 = Idle, 1.0 = Full Turn
var blend_amount := abs(turn_input)
# Update BlendTree parameter
anim_tree.set("parameters/TurnBlend/blend_amount", blend_amount)
# Update physical rotation
rotate_y(-turn_input * turn_speed * delta)---
Expert Pattern: Tree-Complexity-Culler
Optimize massive scenes by swapping the AnimationTree.tree_root resource between a complex "Hero" tree and a simplified "Crowd" tree based on visibility.
class_name AnimationComplexityManager extends Node3D
@export var hero_tree: AnimationRootNode # Complex StateMachine
@export var crowd_tree: AnimationRootNode # Simple Looping Idle
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var visibility: VisibleOnScreenNotifier3D = $VisibleOnScreenNotifier3D
func _ready() -> void:
visibility.screen_entered.connect(func(): anim_tree.tree_root = hero_tree)
visibility.screen_exited.connect(func(): anim_tree.tree_root = crowd_tree)Reference
- Master Skill: godot-master
# advanced_transition_masking.gd
# Using filters (masks) for complex layered animation separation
extends AnimationTree
# This script demonstrates how to dynamically enable/disable filters
# for nodes like AnimationNodeAdd2 or AnimationNodeBlend2.
func enable_upper_body_mask(node_path: String, skeleton: Skeleton3D) -> void:
# This usually refers to an AnimationNode in the BlendTree
var root: AnimationNodeBlendTree = tree_root
var blend_node = root.get_node(node_path)
blend_node.filter_enabled = true
# Enable specific bone paths (e.g. Chest and above)
for i in range(skeleton.get_bone_count()):
var bone_name = skeleton.get_bone_name(i)
if "Spine" in bone_name or "Arm" in bone_name or "Head" in bone_name:
blend_node.set_filter_path("Skeleton3D:" + bone_name, true)
# blendtree_logic_mixing.gd
# Complex BlendTree configuration and dynamic weight mixing [189]
extends AnimationTree
# Expert: Mixing multiple Add2/Blend2 nodes to create a combat layer
# that respects lower body movement.
func set_combat_mix(weight: float) -> void:
# 0.0 = Pure Movement
# 1.0 = Pure Combat/Aim
# Animating multiple weights in sync
set("parameters/UpperBodyBlend/blend_amount", weight)
set("parameters/ArmIKWeight/blend_amount", weight)
# If weight is high, maybe increase the TimeScale of upper body
if weight > 0.8:
set("parameters/CombatSpeed/scale", 1.2)
else:
set("parameters/CombatSpeed/scale", 1.0)
# dynamic_timescale_control.gd
# Expert control of playback speed via AnimationNodeTimeScale
extends AnimationTree
# TimeScale nodes allow independent speed control for specific sub-trees
# (e.g., slowing down feet while keeping upper body fast).
func apply_movement_haste(multiplier: float) -> void:
# multiplier = 1.0 (Normal), 2.0 (Double Speed), 0.5 (Slow Mo)
set("parameters/MovementTime/scale", multiplier)
func bullet_time_transition(target_scale: float, duration: float) -> void:
var tween = create_tween()
# Tweens can target AnimationTree parameters directly!
tween.tween_property(self, "parameters/GlobalTime/scale", target_scale, duration)\
.set_trans(Tween.TRANS_SINE)
# skills/animation-tree-mastery/code/nested_state_machine.gd
extends AnimationTree
## Hierarchical State Machine Expert Pattern
## Technical blueprints for building complex "Sub-Level" states.
func _ready() -> void:
# 1. Hierarchy Logic
# MainSM (Root) -> LocomotionSM (Nested) -> [Idle, Walk, Run]
# 2. Triggering a sub-state transition
_travel_to_locomotion()
func _travel_to_locomotion() -> void:
var state_machine_accessor = get("parameters/playback")
if state_machine_accessor:
state_machine_accessor.travel("Locomotion")
func set_locomotion_speed(speed: float) -> void:
# 3. Driving parameters deep inside nested blend trees
# Expert Path Syntax: "<StateName>/<BlendNodeName>/blend_position"
set("parameters/Locomotion/BlendSpace2D/blend_position", speed)
## EXPERT NOTE:
## Nested state machines prevent "Spider-Web Graph" syndrome.
## Keep 'Locomotion', 'Combat', and 'Interaction' as separate sub-graphs.
# nested_tree_architecture.gd
# Managing hierarchical AnimationNodeStateMachines [258]
extends AnimationTree
func get_sub_machine(parent_name: String) -> AnimationNodeStateMachinePlayback:
# Accessing playback for a nested StateMachine node
return get("parameters/" + parent_name + "/playback")
func travel_sub_state(parent_name: String, sub_state: String) -> void:
var playback = get_sub_machine(parent_name)
if playback:
playback.travel(sub_state)
# Example: Root(Locomotion) -> Sub(Grounded) -> Sub(Walk_Cycle)
func go_to_running_strafe() -> void:
# Travel to Grounded in Root
var root_playback: AnimationNodeStateMachinePlayback = get("parameters/playback")
root_playback.travel("Grounded")
# Travel to Run in Grounded sub-machine
travel_sub_state("Grounded", "Run")
# reactive_oneshot_vfx.gd
# Using AnimationNodeOneShot for high-priority reactive animations
extends AnimationTree
# Use OneShot nodes for non-looping animations that should override the
# current state (recoil, blinks, hit reactions).
func trigger_recoil() -> void:
# OneShot nodes have a 'request' parameter
# AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE = 1
set("parameters/Recoil/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)
func cancel_oneshot(node_name: String) -> void:
# AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT = 2
set("parameters/" + node_name + "/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT)
# root_motion_animtree_sync.gd
# CharacterBody3D synchronization with AnimationTree Root Motion [236]
extends CharacterBody3D
@onready var anim_tree: AnimationTree = $AnimationTree
func _physics_process(delta: float) -> void:
# AnimationTree root motion is often more stable for blending
# than raw AnimationPlayer extraction.
var root_pos = anim_tree.get_root_motion_position()
var root_rot = anim_tree.get_root_motion_rotation()
# Apply rotation first
quaternion *= root_rot
# Transform motion to world space and apply as velocity
var world_motion = (quaternion * root_pos) / delta
velocity.x = world_motion.x
velocity.z = world_motion.z
if not is_on_floor():
velocity.y -= 9.8 * delta
move_and_slide()
# runtime_tree_debugging.gd
# Runtime tool for visualizing and logging current AnimTree states
extends AnimationTree
@export var debug_interval: float = 1.0
var _timer: float = 0.0
func _process(delta: float) -> void:
_timer += delta
if _timer >= debug_interval:
_timer = 0.0
_log_state()
func _log_state() -> void:
var playback: AnimationNodeStateMachinePlayback = get("parameters/playback")
if not playback: return
var current = playback.get_current_node()
var travel = playback.get_travel_path()
print("[AnimTree Debug] Current: %s | Queue: %s" % [current, travel])
# Check specific blend values
var movement_pos = get("parameters/Movement/blend_position")
print(" - Movement Blend: ", movement_pos)
# skills/animation-tree-mastery/code/skeleton_ik_lookat.gd
extends AnimationTree
## Procedural Skeleton IK Expert Pattern
## Technical blueprints for head-tracking using SkeletonModifier3D and AnimationTree.
@export var target_node: Node3D
func _physics_process(_delta: float) -> void:
if not target_node: return
# 1. Drive a 'LookAt' parameter in the AnimationTree
# This assumes a 'SkeletonModifier3D' or an IK node is
# being modulated by this parameter.
var target_pos = target_node.global_position
# 2. Use string-formatted paths to avoid hardcoding
# Pattern: "parameters/LookAt/blend_amount"
set("parameters/LookAt/blend_amount", 1.0)
# 3. Smooth the target vector for the IK solver
var weight = 0.1
_update_ik_bone_pose(target_pos, weight)
func _update_ik_bone_pose(_pos: Vector3, _weight: float) -> void:
# Logic to manually adjust bone transforms if NOT using a node-based IK
pass
## NEVER LIST:
## - NEVER hardcode the parameter string; if the tree structure changes,
## the script will break. Use @onready var paths or constants.
# statemachine_travel_code.gd
# Programmatic StateMachine control via playback object [77]
extends AnimationTree
@onready var state_machine: AnimationNodeStateMachinePlayback = get("parameters/StateMachine/playback")
func switch_to_combat_stance() -> void:
# travel() find the shortest path between current state and target
state_machine.travel("Combat_Idle")
func force_immediate_state(state_name: String) -> void:
# start() bypasses transitions and snaps immediately
state_machine.start(state_name)
func is_in_state(state_name: String) -> bool:
return state_machine.get_current_node() == state_name
func get_next_queued_state() -> String:
# Useful for prediction/UI feedback
return state_machine.get_travel_path().front() if not state_machine.get_travel_path().is_empty() else ""
# sync_group_layering.gd
# Using Sync Groups to keep multi-layered animations aligned [292]
extends AnimationTree
# PROBLEM: Upper body 'Reload' and Lower body 'Walk' have different lengths.
# SOLUTION: Use Sync Groups to force them to share a normalized timeline (0.0 - 1.0).
func setup_reload_sync() -> void:
# This logic usually happens in the BlendTree editor, but scriptable here:
var root: AnimationNodeBlendTree = tree_root
var blend_node = root.get_node("ReloadLayer")
# Enable 'sync' on the Blend2 or Add2 node combining the layers
# This ensures the secondary animation follows the primary's phase.
# Note: In Godot 4, this is the 'sync' property on AnimationNodeSync nodes.
pass # Logic primarily configuration-based
# sync_parameter_manager.gd
# Expert management of AnimationTree parameters with guards [17]
extends AnimationTree
# EXPERT NOTE: Setting parameters every frame via set() without
# checking equality first can cause unnecessary cache invalidation
# on the GPU and logic artifacts.
var _internal_blend_pos: Vector2 = Vector2.ZERO
func update_movement_blend(target_pos: Vector2, lerp_weight: float = 0.1) -> void:
# Smoothly interpolate the blend position in code BEFORE applying to the tree
_internal_blend_pos = _internal_blend_pos.lerp(target_pos, lerp_weight)
# Only update if the difference is significant
var current = get("parameters/Movement/blend_position")
if _internal_blend_pos.distance_to(current) > 0.001:
set("parameters/Movement/blend_position", _internal_blend_pos)
func set_condition_guarded(condition_path: String, value: bool) -> void:
var current = get(condition_path)
if current != value:
set(condition_path, value)
# skills/animation-tree-mastery/scripts/tree_travel_manager.gd
extends Node
## AnimationTree Travel Manager Expert Pattern
## Programmatic state machine transitions with condition caching.
class_name TreeTravelManager
@export var animation_tree: AnimationTree
@export var state_machine_path := "parameters/StateMachine"
var _cached_playback: AnimationNodeStateMachinePlayback
func _ready() -> void:
if animation_tree:
_cached_playback = animation_tree.get(state_machine_path + "/playback")
func travel_to(state_name: String, immediate := false) -> bool:
if not_cached_playback:
push_error("AnimationTree playback not found")
return false
if immediate:
_cached_playback.start(state_name)
else:
_cached_playback.travel(state_name)
return true
func get_current_state() -> String:
if _cached_playback:
return _cached_playback.get_current_node()
return ""
func set_condition(condition_name: String, value: bool) -> void:
if animation_tree:
animation_tree.set(state_machine_path + "/conditions/" + condition_name, value)
func get_condition(condition_name: String) -> bool:
if animation_tree:
return animation_tree.get(state_machine_path + "/conditions/" + condition_name)
return false
func set_blend_position(blend_var: String, position: Vector2) -> void:
if animation_tree:
animation_tree.set("parameters/" + blend_var + "/blend_position", position)
func set_blend_amount(blend_var: String, amount: float) -> void:
if animation_tree:
animation_tree.set("parameters/" + blend_var + "/blend_amount", amount)
## EXPERT USAGE:
## var travel_mgr := TreeTravelManager.new()
## travel_mgr.animation_tree = $AnimationTree
## add_child(travel_mgr)
##
## # State machine transitions
## travel_mgr.travel_to("Walk")
## travel_mgr.set_condition("is_attacking", true)
##
## # Blend space control
## travel_mgr.set_blend_position("MovementBlend", Vector2(velocity.x, velocity.y))