godot-animation-tree-mastery
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAnimationTree Mastery
AnimationTree 精通指南
Expert guidance for Godot's advanced animation blending and state machines.
Godot高级动画混合与状态机的进阶指南。
NEVER Do
绝对不要做这些
- NEVER call on AnimationPlayer when using AnimationTree — AnimationTree controls the player. Directly calling
play()causes conflicts. Useplay()instead.set("parameters/transition_request") - NEVER forget to set — AnimationTree is inactive by default. Animations won't play until
active = true.$AnimationTree.active = true - NEVER use absolute paths for transition_request — Use relative paths. "parameters/StateMachine/transition_request", not "/root/.../transition_request".
- NEVER leave auto_advance enabled unintentionally — Auto-advance transitions fire immediately without conditions. Useful for combo chains, but deadly for idle→walk.
- NEVER use BlendSpace2D for non-directional blending — Use BlendSpace1D for speed (walk→run) or Blend2 for simple tweens. BlendSpace2D is for X+Y axes (strafe animations).
- 使用AnimationTree时,绝对不要在AnimationPlayer上调用—— AnimationTree会控制播放器,直接调用
play()会导致冲突,请改用play()。set("parameters/transition_request") - 绝对不要忘记设置—— AnimationTree默认处于非激活状态,必须设置
active = true后动画才会播放。$AnimationTree.active = true - 绝对不要为transition_request使用绝对路径 —— 请使用相对路径,例如"parameters/StateMachine/transition_request",而非"/root/.../transition_request"。
- 绝对不要无意开启auto_advance —— 自动过渡会在无任何条件的情况下立即触发,适用于连招序列,但在 idle→walk 这类场景中会引发问题。
- 绝对不要将BlendSpace2D用于非定向混合 —— 速度混合(walk→run)请使用BlendSpace1D,简单补间请使用Blend2。BlendSpace2D仅适用于X+Y轴混合(例如 Strafing 动画)。
Available Scripts
可用脚本
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
强制要求:在实现对应模式前,请先阅读相应脚本。
nested_state_machine.gd
nested_state_machine.gd
Hierarchical state machine pattern. Shows travel() between sub-states and deep parameter paths (StateMachine/BlendSpace2D/blend_position).
分层状态机模式,展示了子状态间的travel()调用以及深层参数路径(StateMachine/BlendSpace2D/blend_position)的使用。
skeleton_ik_lookat.gd
skeleton_ik_lookat.gd
Procedural IK for head-tracking. Drives SkeletonModifier3D look-at parameters from AnimationTree with smooth weight blending.
用于头部追踪的程序化IK,通过AnimationTree驱动SkeletonModifier3D的看向参数,并实现平滑权重混合。
Core Concepts
核心概念
AnimationTree Structure
AnimationTree 结构
AnimationTree (node)
├─ Root (assigned in editor)
│ ├─ StateMachine (common)
│ ├─ BlendTree (layering)
│ └─ BlendSpace (directional)
└─ anim_player: NodePath → points to AnimationPlayerAnimationTree (节点)
├─ Root (在编辑器中指定)
│ ├─ StateMachine (常用)
│ ├─ BlendTree (分层动画)
│ └─ BlendSpace (定向混合)
└─ anim_player: NodePath → 指向AnimationPlayerParameter Access
参数访问
gdscript
undefinedgdscript
undefinedSet parameters using string paths
使用字符串路径设置参数
$AnimationTree.set("parameters/StateMachine/transition_request", "run")
$AnimationTree.set("parameters/Movement/blend_position", Vector2(1, 0))
$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")
---var current_state = $AnimationTree.get("parameters/StateMachine/current_state")
---StateMachine Pattern
状态机模式
Basic Setup
基础设置
gdscript
undefinedgdscript
undefinedScene structure:
场景结构:
CharacterBody2D
CharacterBody2D
├─ AnimationPlayer (has: idle, walk, run, jump, land)
├─ AnimationPlayer (包含: idle, walk, run, jump, land)
└─ AnimationTree
└─ AnimationTree
└─ Root: AnimationNodeStateMachine
└─ Root: AnimationNodeStateMachine
StateMachine nodes (created in AnimationTree editor):
状态机节点(在AnimationTree编辑器中创建):
- Idle (AnimationNode referencing "idle")
- Idle (关联"idle"动画的AnimationNode)
- Walk (AnimationNode referencing "walk")
- Walk (关联"walk"动画的AnimationNode)
- Run (AnimationNode referencing "run")
- Run (关联"run"动画的AnimationNode)
- Jump (AnimationNode referencing "jump")
- Jump (关联"jump"动画的AnimationNode)
- Land (AnimationNode referencing "land")
- Land (关联"land"动画的AnimationNode)
@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")undefined@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()
# 基于游戏逻辑的状态过渡
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: # 上升阶段
state_machine.travel("Jump")
else: # 下落阶段
state_machine.travel("Land")undefinedTransition Conditions (Advance Expressions)
过渡条件(高级表达式)
gdscript
undefinedgdscript
undefinedIn AnimationTree editor:
在AnimationTree编辑器中:
Add transition from Idle → Walk
添加从Idle → Walk的过渡
Set "Advance Condition" to "is_walking"
将"Advance Condition"设置为"is_walking"
In code:
代码中:
anim_tree.set("parameters/conditions/is_walking", true)
anim_tree.set("parameters/conditions/is_walking", true)
Transition fires automatically when condition becomes true
当条件变为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
undefinedanim_tree.set("parameters/conditions/is_damaged", false) # 每帧重置
func take_damage() -> void:
anim_tree.set("parameters/conditions/is_damaged", true)
# 会立即触发向"Hurt"状态的过渡
undefinedAuto-Advance (Combo Chains)
自动过渡(连招序列)
gdscript
undefinedgdscript
undefinedIn AnimationTree editor:
在AnimationTree编辑器中:
Transition from Attack1 → Attack2
添加从Attack1 → Attack2的过渡
Enable "Auto Advance" (no condition needed)
开启"Auto Advance"(无需设置条件)
Code:
代码:
state_machine.travel("Attack1")
state_machine.travel("Attack1")
Attack1 animation plays
播放Attack1动画
When Attack1 finishes, automatically transitions to Attack2
Attack1播放完毕后,自动过渡到Attack2
When Attack2 finishes, transitions to Idle (next auto-advance)
Attack2播放完毕后,自动过渡到Idle(下一个自动过渡)
Useful for:
适用场景:
- Attack combos
- 攻击连招
- Death → Respawn
- 死亡→重生
- Cutscene sequences
- 过场动画序列
---
---BlendSpace2D (Directional Movement)
BlendSpace2D(定向移动)
8-Way Movement
八方向移动
gdscript
undefinedgdscript
undefinedCreate BlendSpace2D in AnimationTree editor:
在AnimationTree编辑器中创建BlendSpace2D:
- Add animations at positions:
- 在以下位置添加动画:
- (0, -1): walk_up
- (0, -1): walk_up
- (0, 1): walk_down
- (0, 1): walk_down
- (-1, 0): walk_left
- (-1, 0): walk_left
- (1, 0): walk_right
- (1, 0): walk_right
- (-1, -1): walk_up_left
- (-1, -1): walk_up_left
- (1, -1): walk_up_right
- (1, -1): walk_up_right
- (-1, 1): walk_down_left
- (-1, 1): walk_down_left
- (1, 1): walk_down_right
- (1, 1): walk_down_right
- (0, 0): idle (center)
- (0, 0): idle(中心位置)
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_upundefinedfunc _physics_process(delta: float) -> void:
var input := Input.get_vector("left", "right", "up", "down")
# 设置混合位置(AnimationTree会自动在动画间插值)
anim_tree.set("parameters/Movement/blend_position", input)
# BlendSpace2D会根据输入自动混合动画
# input = (0.5, -0.5) → 混合walk_right与walk_up动画undefinedBlendSpace1D (Speed Blending)
BlendSpace1D(速度混合)
gdscript
undefinedgdscript
undefinedFor walk → run transitions
用于walk → run的过渡
Create BlendSpace1D:
创建BlendSpace1D:
- Position 0.0: walk
- 位置0.0: walk
- Position 1.0: run
- 位置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
---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)
# 随着速度提升,平滑从walk过渡到run
---BlendTree (Layered Animations)
BlendTree(分层动画)
Add Upper Body Animation
叠加上半身动画
gdscript
undefinedgdscript
undefinedProblem: Want to aim gun while walking
需求:在行走时同时实现持枪瞄准
Solution: Blend upper body (aim) with lower body (walk)
解决方案:将上半身(瞄准)与下半身(行走)动画混合
In AnimationTree editor:
在AnimationTree编辑器中:
Root → BlendTree
Root → BlendTree
├─ Walk (lower body animation)
├─ Walk(下半身动画)
├─ Aim (upper body animation)
├─ Aim(上半身动画)
└─ Add2 node (combines them)
└─ Add2节点(将两者组合)
- Inputs: Walk, Aim
- 输入: Walk, Aim
- filter_enabled: true
- filter_enabled: true
- Filters: Only enable upper body bones for Aim
- 过滤规则: 仅启用Aim动画的上半身骨骼
Code:
代码:
No code needed! BlendTree auto-combines
无需额外代码!BlendTree会自动组合动画
Just ensure animations are assigned
只需确保已正确分配动画即可
undefinedundefinedBlend2 (Crossfade)
Blend2(交叉淡入淡出)
gdscript
undefinedgdscript
undefinedBlend between two animations dynamically
动态混合两个动画
Root → BlendTree
Root → BlendTree
└─ Blend2
└─ Blend2
├─ Input A: idle
├─ 输入A: idle
└─ Input B: attack
└─ 输入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
---var blend_amount := 0.0
func _process(delta: float) -> void:
# 逐渐从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
AnimationTree 根运动
gdscript
undefinedgdscript
undefinedEnable in AnimationTree
在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()
---anim_tree.root_motion_track = NodePath("CharacterBody3D/Skeleton3D:Root")
func _physics_process(delta: float) -> void:
# 获取根运动数据
var root_motion := anim_tree.get_root_motion_position()
# 应用到角色(不要与velocity混用!)
global_position += root_motion.rotated(rotation.y)
# 对于使用move_and_slide的CharacterBody3D:
velocity = root_motion / delta
move_and_slide()
---Advanced Patterns
进阶模式
Sub-StateMachines
子状态机
gdscript
undefinedgdscript
undefinedNested state machines for complex behavior
用于复杂行为的嵌套状态机
Root → StateMachine
Root → StateMachine
├─ Grounded (Sub-StateMachine)
├─ Grounded(子状态机)
│ ├─ Idle
│ ├─ Idle
│ ├─ Walk
│ ├─ Walk
│ └─ Run
│ └─ Run
└─ Airborne (Sub-StateMachine)
└─ Airborne(子状态机)
├─ Jump
├─ Jump
├─ Fall
├─ Fall
└─ Glide
└─ Glide
Access nested states:
访问嵌套状态:
var sub_state = anim_tree.get("parameters/Grounded/playback")
sub_state.travel("Run")
undefinedvar sub_state = anim_tree.get("parameters/Grounded/playback")
sub_state.travel("Run")
undefinedTime Scale (Slow Motion)
时间缩放(慢动作)
gdscript
undefinedgdscript
undefinedSlow down specific animation without affecting others
单独放慢指定动画,不影响其他动画
anim_tree.set("parameters/TimeScale/scale", 0.5) # 50% speed
anim_tree.set("parameters/TimeScale/scale", 0.5) # 50%速度
Useful for:
适用场景:
- Bullet time
- 子弹时间
- Hurt/stun effects
- 受伤/眩晕效果
- Charge-up animations
- 蓄力动画
undefinedundefinedSync Between Animations
动画同步
gdscript
undefinedgdscript
undefinedProblem: Switching from walk → run causes foot slide
问题:从walk切换到run时会出现脚部滑动
Solution: Use "Sync" on transition
解决方案:在过渡中启用"Sync"
In AnimationTree editor:
在AnimationTree编辑器中:
Transition: Walk → Run
过渡: Walk → Run
Enable "Sync" checkbox
勾选"Sync"复选框
Godot automatically syncs animation playback positions
Godot会自动同步动画播放位置
Feet stay grounded during transition
过渡过程中脚部会保持着地状态
---
---Debugging AnimationTree
AnimationTree 调试
Print Current State
打印当前状态
gdscript
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)gdscript
func _process(delta: float) -> void:
var current_state = anim_tree.get("parameters/StateMachine/current_state")
print("当前状态: ", current_state)
# 打印混合位置
var blend_pos = anim_tree.get("parameters/Movement/blend_position")
print("混合位置: ", blend_pos)Common Issues
常见问题
gdscript
undefinedgdscript
undefinedIssue: Animation not playing
问题:动画无法播放
Solution:
解决方案:
if not anim_tree.active:
anim_tree.active = true
if not anim_tree.active:
anim_tree.active = true
Issue: Transition not working
问题:过渡不生效
Check:
检查:
1. Is advance_condition set?
1. 是否设置了advance_condition?
2. Is transition priority correct?
2. 过渡优先级是否正确?
3. Is auto_advance enabled unintentionally?
3. 是否无意开启了auto_advance?
Issue: Blend not smooth
问题:混合效果不流畅
Solution: Increase transition xfade_time (0.1 - 0.3s)
解决方案:增加过渡的xfade_time(0.1-0.3秒)
---
---Performance Optimization
性能优化
Disable When Not Needed
无需使用时禁用
gdscript
undefinedgdscript
undefinedAnimationTree is expensive
AnimationTree性能开销较大
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
---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
决策树:何时使用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
| 特性 | 仅使用AnimationPlayer | 使用AnimationTree |
|---|---|---|
| 简单状态切换 | ✅ 使用play("idle") | ❌ 过于冗余 |
| 定向移动 | ❌ 实现复杂 | ✅ 推荐使用BlendSpace2D |
| 状态机(5个以上状态) | ❌ 代码混乱 | ✅ 推荐使用StateMachine |
| 分层动画 | ❌ 需手动混合 | ✅ 推荐使用BlendTree |
| 根运动 | ✅ 可实现 | ✅ 原生支持 |
| 过渡混合 | ❌ 需手动实现 | ✅ 自动支持 |
推荐使用AnimationTree的场景:包含5个以上状态的复杂角色、定向移动、分层动画
推荐使用AnimationPlayer的场景:简单动画、UI、过场动画、道具
Reference
参考
- Master Skill: godot-master
- 核心技能:godot-master