重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
godot-animation-tree-mastery by thedivergentai/gd-agentic-skills
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-animation-tree-masteryGodot 高级动画混合与状态机的专家指南。
play() — AnimationTree 控制着播放器。直接调用 play() 会导致冲突和抖动。请改用 set("parameters/transition_request") 或 travel() [12]。active = true — AnimationTree 默认是未激活的。在设置 $AnimationTree.active = true 之前,动画不会播放 [13]。"parameters/StateMachine/transition_request"。这确保了当节点在层级结构中移动时的兼容性 [14]。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
auto_advanceBlendSpace2D 进行 1D 混合 — 只混合速度?请使用 BlendSpace1D。只混合两个状态?请使用 Blend2。BlendSpace2D 专门用于 X+Y 方向输入(侧向移动)[16, 142]。AnimationTree 参数 — 无论是否改变,每帧都通过 set() 设置参数会导致缓存失效和潜在的卡顿。请先检查是否相等。BlendTrees — 每一层都会增加 CPU 开销。如果逻辑可以在 StateMachine 或简单的脚本驱动的 Blend2 中处理,就在那里处理。await get_tree().process_frame — 有时树需要一帧时间来协调状态,然后下一个参数更改才会生效。auto_advance 处理过场动画 — 如果动画被中断,auto_advance 可能会使角色处于损坏状态。请改用 Method Tracks 来发出状态完成信号。Sync 组 — 这会强制一个动画以极快的速度播放。对于不匹配的循环,请使用 TimeScale 或单独的层。强制要求:在实现相应模式之前,请阅读适当的脚本。
对 AnimationTree 参数进行专家级管理,带有防护机制以防止冗余更新和 GPU 缓存抖动。
使用 AnimationNodeOneShot 处理高优先级反应性动画,如后坐力、眨眼和受击反应。
运行时操作播放速度,用于子弹时间效果或移动速度倍增器。
对 Add2 等节点进行程序化骨骼过滤(遮罩),以分离上半身/下半身动画。
使用 travel() 和 start() 对 AnimationNodeStateMachinePlayback 进行程序化控制。
BlendTree 节点的复杂混合模式,用于创建交互式战斗层。
专门为 AnimationTree 节点优化的专家级 3D CharacterBody 运动提取。
使用同步组来保持多层动画(例如行走和装弹)完美对齐。
用于管理分层状态机和嵌套节点参数路径的模式。
用于实时可视化当前状态、过渡路径和混合值的交互式工具。
AnimationTree (node)
├─ Root (assigned in editor)
│ ├─ StateMachine (common)
│ ├─ BlendTree (layering)
│ └─ BlendSpace (directional)
└─ anim_player: NodePath → points to AnimationPlayer
# 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")
# 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")
# 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 immediately
# 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
# 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_up
# 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
# 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 assigned
# 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
# 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()
# 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")
# 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 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
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)
# 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)
# 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
| 功能 | 仅使用 AnimationPlayer | 使用 AnimationTree |
|---|---|---|
| 简单的状态切换 | ✅ play("idle") | ❌ 杀鸡用牛刀 |
| 方向性移动 | ❌ 代码复杂 | ✅ BlendSpace2D |
| 状态机(5个以上状态) | ❌ 代码混乱 | ✅ StateMachine |
| 分层动画 | ❌ 手动混合 | ✅ BlendTree |
| 根运动 | ✅ 可能实现 | ✅ 内置支持 |
| 过渡混合 | ❌ 手动处理 | ✅ 自动处理 |
使用 AnimationTree 的场景:具有 5 个以上状态的复杂角色、方向性移动、分层动画 使用 AnimationPlayer 的场景:简单动画、UI、过场动画、道具
每周安装量
71
代码仓库
GitHub 星标数
59
首次出现
2026年2月10日
安全审计
安装于
opencode70
gemini-cli69
codex69
kimi-cli68
github-copilot68
amp68
Expert guidance for Godot's advanced animation blending and state machines.
play() on AnimationPlayer when using AnimationTree — AnimationTree controls the player. Directly calling play() causes conflicts and jitter. Use set("parameters/transition_request") or travel() instead [12].active = true — AnimationTree is inactive by default. Animations won't play until $AnimationTree.active = true [13]."parameters/StateMachine/transition_request". This ensures compatibility when nodes move in the hierarchy [14].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].BlendSpace2D for 1D blending — Blending only speed? Use BlendSpace1D. Blending only two states? Use Blend2. BlendSpace2D is specifically for X+Y directional inputs (strafe) [16, 142].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.BlendTrees for simple logic — Every layer adds CPU overhead. If logic can be handled in a StateMachine or a simple script-driven Blend2, do it there.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.auto_advance for long cutscenes — If an animation is interrupted, auto_advance can put the character in a broken state. Use Method Tracks to signal state completion instead.Sync groups for animations with wildly different lengths — It forces one animation to play at an extreme speed. Use TimeScale or separate layers for mismatching cycles.MANDATORY : Read the appropriate script before implementing the corresponding pattern.
Expert management of AnimationTree parameters with guards to prevent redundant updates and GPU cache churn.
Using AnimationNodeOneShot for high-priority reactive animations like recoil, blinks, and hit reactions.
Runtime manipulation of playback speed for bullet-time effects or movement haste multipliers.
Procedural bone filtering (masking) for nodes like Add2 to separate upper/lower body animations.
Programmatic control of AnimationNodeStateMachinePlayback using travel() and start().
Complex mixing patterns for BlendTree nodes to create interactive combat layers.
Expert 3D CharacterBody motion extraction optimized specifically for AnimationTree nodes.
Using Sync Groups to keep multi-layered animations (e.g. walk and reload) perfectly aligned.
Pattern for managing hierarchical State Machines and nested node parameter paths.
Interactive tool for visualizing current states, transition paths, and blend values in real-time.
AnimationTree (node)
├─ Root (assigned in editor)
│ ├─ StateMachine (common)
│ ├─ BlendTree (layering)
│ └─ BlendSpace (directional)
└─ anim_player: NodePath → points to AnimationPlayer
# 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")
# 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")
# 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 immediately
# 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
# 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_up
# 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
# 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 assigned
# 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
# 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()
# 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")
# 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 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
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)
# 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)
# 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
| 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
Weekly Installs
71
Repository
GitHub Stars
59
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode70
gemini-cli69
codex69
kimi-cli68
github-copilot68
amp68
Go并发编程最佳实践指南:goroutine、channel与同步原语详解
784 周安装