303 lines
9.1 KiB
Markdown
303 lines
9.1 KiB
Markdown
---
|
|
name: godot-animation
|
|
description: "Animation systems for Godot 4.x: AnimationPlayer vs AnimationTree, state machines, blend spaces (1D/2D/directional), animation libraries, root motion, collapsible tracks, and tween_await(). Use when building character animations, cutscenes, or visual effects in Godot."
|
|
license: MIT
|
|
compatibility:
|
|
- godot-4.0
|
|
- godot-4.1
|
|
- godot-4.2
|
|
- godot-4.3
|
|
- godot-4.4
|
|
- godot-4.5
|
|
- godot-4.6
|
|
- godot-4.7
|
|
metadata:
|
|
author: vl4dt
|
|
version: 0.1.0
|
|
tags:
|
|
- godot
|
|
- animation
|
|
- animationplayer
|
|
- animationtree
|
|
- blend-space
|
|
- root-motion
|
|
created: 2026-06-20
|
|
---
|
|
|
|
# Animation Systems for Godot 4.x
|
|
|
|
Comprehensive patterns for character animation, state machines, and visual effects in Godot 4.x.
|
|
|
|
## AnimationPlayer vs AnimationTree
|
|
|
|
### AnimationPlayer — Simple Animations
|
|
|
|
Use AnimationPlayer for linear sequences, cutscenes, and simple property animations.
|
|
|
|
```gdscript
|
|
@onready var anim_player = $AnimationPlayer
|
|
|
|
func play_attack():
|
|
anim_player.play("attack")
|
|
|
|
func play_transition(target: String, fade_time: float = 0.2):
|
|
anim_player.play(target, fade_time)
|
|
|
|
# Signal-based animation completion
|
|
func _ready():
|
|
anim_player.animation_finished.connect(_on_animation_finished)
|
|
|
|
func _on_animation_finished(anim_name: String):
|
|
if anim_name == "attack":
|
|
print("Attack animation complete")
|
|
```
|
|
|
|
### AnimationTree — State Machine Animations
|
|
|
|
Use AnimationTree for complex character animations with branching states (idle, walk, run, jump).
|
|
|
|
```gdscript
|
|
@onready var anim_tree = $AnimationTree
|
|
@onready var anim_state = anim_tree.get("state_machine")
|
|
|
|
func _ready():
|
|
anim_state.travel("Idle") # Start at Idle state
|
|
|
|
func set_animation(state_name: String):
|
|
if anim_state.has_state(state_name):
|
|
anim_state.travel(state_name)
|
|
|
|
func _physics_process(delta: float):
|
|
var speed = velocity.length()
|
|
anim_tree.set("parameters/Move/speed", speed)
|
|
anim_tree.process(delta)
|
|
```
|
|
|
|
### When to Use Which
|
|
|
|
| Use Case | Tool |
|
|
|----------|------|
|
|
| Cutscenes, UI animations | AnimationPlayer |
|
|
| Character movement states | AnimationTree + StateMachine |
|
|
| Simple one-shot effects | AnimationPlayer |
|
|
| Blending between movements | AnimationTree + BlendSpace2D |
|
|
| Complex combat combos | AnimationTree + AnimationNodeBlendTree |
|
|
|
|
## State Machines in AnimationTree
|
|
|
|
### Building a Character State Machine
|
|
|
|
```gdscript
|
|
# In the AnimationTree, create an AnimationNodeStateMachine:
|
|
# States: Idle, Walk, Run, Jump, Fall, Attack
|
|
# Transitions: Idle <-> Walk, Idle <-> Run, Walk -> Run, etc.
|
|
|
|
@onready var anim_tree = $AnimationTree
|
|
@onready var state_machine = anim_tree.get("state_machine")
|
|
|
|
func _physics_process(delta: float):
|
|
var speed = velocity.length()
|
|
var new_state = "Idle"
|
|
|
|
if not is_on_floor():
|
|
new_state = "Fall" if velocity.y < 0 else "Jump"
|
|
elif speed > 0.1:
|
|
new_state = "Run" if speed > run_threshold else "Walk"
|
|
|
|
# Only transition if the target state exists
|
|
if state_machine.has_state(new_state):
|
|
state_machine.travel(new_state)
|
|
|
|
anim_tree.set("parameters/Move/speed", speed)
|
|
anim_tree.process(delta)
|
|
```
|
|
|
|
### Transition Conditions
|
|
|
|
Set transition conditions in the AnimationTree editor:
|
|
- **Blend**: Use blend amounts based on parameters (e.g., speed)
|
|
- **One Shot**: Play once then return (for attacks, interactions)
|
|
- **Transition Rules**: Define when transitions are allowed
|
|
|
|
## Blend Spaces
|
|
|
|
### 1D Blend Space
|
|
|
|
Use for animations ordered along a single axis (e.g., walk speed).
|
|
|
|
```gdscript
|
|
# AnimationTree setup:
|
|
# BlendSpace1D: Walk -> Run -> Sprint
|
|
# Parameter: "speed" (float)
|
|
|
|
anim_tree.set("parameters/BlendSpace1D/speed", current_speed)
|
|
```
|
|
|
|
### 2D Blend Space
|
|
|
|
Use for directional movement blending.
|
|
|
|
```gdscript
|
|
# AnimationTree setup:
|
|
# BlendSpace2D: X = speed, Y = direction blend
|
|
# Parameters: "speed" and "direction"
|
|
|
|
anim_tree.set("parameters/BlendSpace2D/speed", velocity.length())
|
|
anim_tree.set("parameters/BlendSpace2D/direction", velocity.angle())
|
|
```
|
|
|
|
### Directional Blend Space (Godot 4.x)
|
|
|
|
Use a directional blend for 8-directional character animations.
|
|
|
|
```gdscript
|
|
# Set up AnimationNodeBlendDBone2D for directional blending
|
|
@onready var anim_tree = $AnimationTree
|
|
|
|
func _physics_process(delta: float):
|
|
var dir = velocity.normalized()
|
|
anim_tree.set("parameters/Directional/speed", velocity.length())
|
|
anim_tree.set("parameters/Directional/direction", dir)
|
|
anim_tree.process(delta)
|
|
```
|
|
## Animation Libraries and Blending Tracks
|
|
|
|
### Animation Libraries
|
|
|
|
Group related animations into libraries for organization.
|
|
|
|
```gdscript
|
|
# Access library animations by name
|
|
@onready var anim_player = $AnimationPlayer
|
|
|
|
func play_animation(anim_name: String):
|
|
# Animations are organized by library in the editor
|
|
anim_player.play(anim_name)
|
|
|
|
# List available animations
|
|
func get_available_animations() -> Array:
|
|
return anim_player.get_animation_list()
|
|
```
|
|
|
|
### Blending Tracks
|
|
|
|
Blend between multiple animation tracks for smooth transitions.
|
|
|
|
```gdscript
|
|
# Set blend amount in AnimationTree
|
|
anim_tree.set("parameters/Idle/blend_amount", 1.0)
|
|
|
|
# Blend between two animations
|
|
anim_player.play("idle", -1.0, 1.0) # loop, 1s fade-in
|
|
anim_player.play("walk", -1.0, 0.5) # loop, 0.5s fade-in
|
|
|
|
# Crossfade by playing both simultaneously
|
|
# The AnimationTree blend space handles the mixing
|
|
```
|
|
## Root Motion for Character Movement
|
|
|
|
### Using Root Motion in AnimationTree
|
|
|
|
Root motion applies bone animation directly to the character transform.
|
|
|
|
```gdscript
|
|
class_name RootMotionCharacter extends CharacterBody3D
|
|
|
|
@onready var anim_tree = $AnimationTree
|
|
@onready var anim_state = anim_tree.get("state_machine")
|
|
|
|
@export var root_motion_enabled: bool = true
|
|
|
|
func _physics_process(delta: float):
|
|
# Calculate root motion displacement
|
|
var prev_transform = global_transform
|
|
|
|
# Process animation tree (applies root motion)
|
|
anim_tree.process(delta)
|
|
|
|
if root_motion_enabled:
|
|
var root_delta = global_transform.xform_inv(prev_transform)
|
|
velocity = Vector3(root_delta.origin.x / delta, 0, root_delta.origin.z / delta)
|
|
else:
|
|
# Use scripted movement instead
|
|
velocity = transform.basis.z * speed
|
|
|
|
move_and_slide()
|
|
```
|
|
|
|
### When to Use Root Motion
|
|
- Platformer jump arcs (precise landing positions)
|
|
- Attack animations with fixed reach distances
|
|
- Cutscene character movements
|
|
|
|
### When to Avoid Root Motion
|
|
- Multiplayer games (server authority conflicts)
|
|
- Characters that need precise position control
|
|
- Games with custom physics responses
|
|
## Godot 4.7 Animation Features
|
|
|
|
### Collapsible Animation Tracks
|
|
|
|
Godot 4.7 introduces collapsible animation tracks for better organization in the AnimationPlayer editor.
|
|
|
|
```gdscript
|
|
# No code change needed — this is an editor feature
|
|
# In the AnimationPlayer inspector, click track headers to collapse/expand
|
|
# Group related properties under collapsible sections
|
|
```
|
|
|
|
### tween_await() for Sequential Animations
|
|
|
|
Use await with tweens for clean sequential animation chains.
|
|
|
|
```gdscript
|
|
func play_combo_animation():
|
|
# Chain animations without nested callbacks
|
|
await create_tween().tween_property($Sprite, "offset:x", 10.0, 0.1).finished
|
|
await create_tween().tween_property($Sprite, "offset:x", 0.0, 0.1).finished
|
|
print("Combo animation complete")
|
|
|
|
# Or with AnimationPlayer
|
|
func play_with_wait():
|
|
var tween = create_tween()
|
|
anim_player.play("attack")
|
|
await tween.tween_interval(0.5)
|
|
anim_player.play("recovery")
|
|
```
|
|
## Animation Performance Tips
|
|
|
|
- Use **AnimationTree** with state machines instead of multiple AnimationPlayers
|
|
- Keep animation libraries small and focused
|
|
- Use **AnimationNodeSync** to synchronize multiple character animations
|
|
- Preload animation resources with preload() for always-needed scenes
|
|
- Avoid animating properties that can be set directly (e.g., position vs tween)
|
|
- Use `animation_finished` signal instead of polling for completion
|
|
|
|
## Quick Reference: Animation Methods
|
|
|
|
| Method | Use Case |
|
|
|--------|----------|
|
|
| `anim_player.play(name)` | Start an animation |
|
|
| `anim_player.play(name, fade_time)` | Crossfade to animation |
|
|
| `anim_tree.process(delta)` | Update state machine |
|
|
| `anim_tree.set("params/...", value)` | Set blend parameters |
|
|
| `state_machine.travel(name)` | Switch to a state |
|
|
| `create_tween()` | Create animation tweens |
|
|
| `await tween.finished` | Wait for tween completion |
|
|
|
|
## MCP Bridge Tools (Optional — Live Editor Integration)
|
|
|
|
When the MCP bridge is running (Phase 2), these tools enhance animation debugging:
|
|
|
|
- **`animation_play`** — Play any animation from the project without opening the editor
|
|
- **`animation_list`** — List all available animations and their libraries
|
|
- **`animation_state_get`** — Query current AnimationTree state machine status
|
|
- **`animation_preview_frame`** — Preview a specific frame of any animation
|
|
|
|
> **Note:** MCP tools require the Godot editor to be running with the MCP plugin enabled. Skills work independently without the bridge.
|
|
|
|
## References
|
|
|
|
- [AnimationPlayer Documentation](references/animationplayer-guide.md)
|
|
- [AnimationTree State Machine Patterns](references/animationtree-states.md)
|
|
- [Blend Space Design Guide](references/blend-spaces.md) |