animation-system

Implements animation systems using AnimationPlayer, AnimationTree, blend trees, and procedural animation. Use when creating character animations and visual effects.

16 stars

Best use case

animation-system is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Implements animation systems using AnimationPlayer, AnimationTree, blend trees, and procedural animation. Use when creating character animations and visual effects.

Teams using animation-system should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/animation-system/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/content-media/animation-system/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/animation-system/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How animation-system Compares

Feature / Agentanimation-systemStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Implements animation systems using AnimationPlayer, AnimationTree, blend trees, and procedural animation. Use when creating character animations and visual effects.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Godot Animation System

When implementing animations, use these patterns for smooth and responsive character movement.

## AnimationPlayer Basics

### Playing Animations
```gdscript
extends CharacterBody2D

@onready var anim_player: AnimationPlayer = $AnimationPlayer

func _ready() -> void:
    # Connect to animation finished signal
    anim_player.animation_finished.connect(_on_animation_finished)

func play_animation(anim_name: String, speed: float = 1.0) -> void:
    if anim_player.current_animation != anim_name:
        anim_player.play(anim_name)
        anim_player.speed_scale = speed

func play_backwards(anim_name: String) -> void:
    anim_player.play_backwards(anim_name)

func stop_animation() -> void:
    anim_player.stop()

func pause_animation() -> void:
    anim_player.pause()

func resume_animation() -> void:
    anim_player.play()

func _on_animation_finished(anim_name: String) -> void:
    match anim_name:
        "attack":
            play_animation("idle")
        "death":
            queue_free()
```

### Animation Blending
```gdscript
extends AnimationPlayer

func crossfade_to(anim_name: String, duration: float = 0.2) -> void:
    if current_animation == anim_name:
        return

    # Queue the new animation with crossfade
    queue(anim_name)
    advance(0)  # Start immediately

    # Use AnimationPlayer's built-in blending
    set_blend_time(current_animation, anim_name, duration)

# Manual crossfade using AnimationTree is preferred for complex blending
```

### Animation Callbacks
```gdscript
extends CharacterBody2D

@onready var anim_player: AnimationPlayer = $AnimationPlayer

func _ready() -> void:
    # Add method track call in animation
    # Or use animation_finished signal

    pass

# Called from animation track
func spawn_projectile() -> void:
    var projectile := preload("res://projectile.tscn").instantiate()
    projectile.global_position = $ProjectileSpawn.global_position
    get_parent().add_child(projectile)

func play_sound(sound_name: String) -> void:
    var sound := $Sounds.get_node(sound_name) as AudioStreamPlayer2D
    if sound:
        sound.play()

func enable_hitbox() -> void:
    $Hitbox/CollisionShape2D.disabled = false

func disable_hitbox() -> void:
    $Hitbox/CollisionShape2D.disabled = true
```

## AnimationTree

### State Machine Setup
```gdscript
extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree
@onready var state_machine: AnimationNodeStateMachinePlayback = \
    anim_tree.get("parameters/playback")

func _ready() -> void:
    anim_tree.active = true

func travel_to_state(state_name: String) -> void:
    state_machine.travel(state_name)

func force_state(state_name: String) -> void:
    state_machine.start(state_name)

func get_current_state() -> String:
    return state_machine.get_current_node()

func is_playing(state_name: String) -> bool:
    return state_machine.get_current_node() == state_name

func _physics_process(_delta: float) -> void:
    update_animation_state()

func update_animation_state() -> void:
    if not is_on_floor():
        if velocity.y < 0:
            travel_to_state("Jump")
        else:
            travel_to_state("Fall")
    elif velocity.length() > 10:
        travel_to_state("Run")
    else:
        travel_to_state("Idle")
```

### Blend Tree Setup
```gdscript
extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree

func _physics_process(_delta: float) -> void:
    update_blend_parameters()

func update_blend_parameters() -> void:
    # For BlendSpace2D (8-directional movement)
    var input := Input.get_vector("left", "right", "up", "down")
    anim_tree.set("parameters/Move/blend_position", input)

    # For BlendSpace1D (speed-based)
    var speed_ratio := velocity.length() / max_speed
    anim_tree.set("parameters/Speed/blend_position", speed_ratio)

    # For animation transitions
    anim_tree.set("parameters/conditions/is_jumping", not is_on_floor() and velocity.y < 0)
    anim_tree.set("parameters/conditions/is_falling", not is_on_floor() and velocity.y > 0)
    anim_tree.set("parameters/conditions/is_grounded", is_on_floor())
```

### One-Shot Animations
```gdscript
extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree

func play_attack() -> void:
    # Trigger one-shot animation
    anim_tree.set("parameters/Attack/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)

func abort_attack() -> void:
    anim_tree.set("parameters/Attack/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT)

func is_attacking() -> bool:
    return anim_tree.get("parameters/Attack/active")

func _on_attack_finished() -> void:
    # Called when one-shot completes
    pass
```

### Layered Animations
```gdscript
# AnimationTree structure:
# - AnimationNodeBlendTree
#   - Add2 (blend lower and upper body)
#     - Input 0: State Machine (lower body: idle, walk, run)
#     - Input 1: State Machine (upper body: idle, aim, shoot)
#     - Filter: Upper body bones only

extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree

func _ready() -> void:
    # Set up bone filter for upper body layer
    # This is usually done in editor, but can be done in code
    pass

func update_animations() -> void:
    # Lower body follows movement
    var move_state := "idle" if velocity.length() < 10 else "run"
    anim_tree.set("parameters/LowerBody/playback").travel(move_state)

    # Upper body independent
    if is_aiming:
        anim_tree.set("parameters/UpperBody/playback").travel("aim")
    elif is_shooting:
        anim_tree.set("parameters/UpperBody/playback").travel("shoot")
    else:
        anim_tree.set("parameters/UpperBody/playback").travel("idle")

    # Blend amount
    anim_tree.set("parameters/Add2/add_amount", 1.0 if is_aiming else 0.0)
```

## Procedural Animation

### Look At / Head Tracking
```gdscript
extends Node3D

@export var head_bone: String = "Head"
@export var max_angle := 70.0
@export var look_speed := 5.0

var skeleton: Skeleton3D
var head_bone_idx: int
var target: Node3D

func _ready() -> void:
    skeleton = $Skeleton3D
    head_bone_idx = skeleton.find_bone(head_bone)

func _process(delta: float) -> void:
    if not target or head_bone_idx < 0:
        return

    var head_transform := skeleton.get_bone_global_pose(head_bone_idx)
    var head_position := skeleton.to_global(head_transform.origin)

    var target_direction := (target.global_position - head_position).normalized()
    var local_direction := skeleton.global_transform.basis.inverse() * target_direction

    # Calculate rotation to look at target
    var target_rotation := Quaternion(Vector3.FORWARD, local_direction)

    # Clamp rotation
    var angle := target_rotation.get_euler()
    angle.x = clamp(angle.x, deg_to_rad(-max_angle), deg_to_rad(max_angle))
    angle.y = clamp(angle.y, deg_to_rad(-max_angle), deg_to_rad(max_angle))

    var clamped_rotation := Quaternion.from_euler(angle)

    # Apply smooth rotation
    var current := skeleton.get_bone_pose_rotation(head_bone_idx)
    var new_rotation := current.slerp(clamped_rotation, look_speed * delta)

    skeleton.set_bone_pose_rotation(head_bone_idx, new_rotation)
```

### Procedural Walk Cycle
```gdscript
extends CharacterBody2D

@export var leg_length := 20.0
@export var step_height := 10.0
@export var step_duration := 0.3

@onready var left_foot: Node2D = $LeftFoot
@onready var right_foot: Node2D = $RightFoot

var left_foot_target: Vector2
var right_foot_target: Vector2
var step_progress := 0.0
var is_left_stepping := true

func _physics_process(delta: float) -> void:
    if velocity.length() > 10:
        update_procedural_walk(delta)
    else:
        reset_feet()

func update_procedural_walk(delta: float) -> void:
    step_progress += delta / step_duration

    if step_progress >= 1.0:
        step_progress = 0.0
        is_left_stepping = not is_left_stepping
        calculate_next_step()

    # Interpolate foot positions
    var stepping_foot := left_foot if is_left_stepping else right_foot
    var grounded_foot := right_foot if is_left_stepping else left_foot
    var target := left_foot_target if is_left_stepping else right_foot_target

    # Arc motion for stepping foot
    var t := step_progress
    var horizontal := stepping_foot.position.lerp(target, t)
    var vertical_offset := sin(t * PI) * step_height

    stepping_foot.position = horizontal + Vector2(0, -vertical_offset)

func calculate_next_step() -> void:
    var forward := velocity.normalized()
    var step_distance := velocity.length() * step_duration

    if is_left_stepping:
        left_foot_target = left_foot.position + forward * step_distance
    else:
        right_foot_target = right_foot.position + forward * step_distance
```

### Squash and Stretch
```gdscript
extends Sprite2D

@export var squash_amount := 0.3
@export var stretch_amount := 0.2
@export var return_speed := 10.0

var target_scale := Vector2.ONE

func squash() -> void:
    target_scale = Vector2(1 + squash_amount, 1 - squash_amount)

func stretch() -> void:
    target_scale = Vector2(1 - stretch_amount, 1 + stretch_amount)

func _process(delta: float) -> void:
    scale = scale.lerp(target_scale, return_speed * delta)
    target_scale = target_scale.lerp(Vector2.ONE, return_speed * delta)

# Usage with physics
extends CharacterBody2D

func _physics_process(delta: float) -> void:
    var was_on_floor := is_on_floor()
    move_and_slide()

    # Landing squash
    if is_on_floor() and not was_on_floor:
        $Sprite2D.squash()

    # Jumping stretch
    if Input.is_action_just_pressed("jump") and is_on_floor():
        $Sprite2D.stretch()
```

### Screen Shake
```gdscript
extends Camera2D

var shake_amount := 0.0
var shake_decay := 5.0

func shake(amount: float, duration: float = 0.2) -> void:
    shake_amount = amount
    var tween := create_tween()
    tween.tween_property(self, "shake_amount", 0.0, duration)

func _process(delta: float) -> void:
    if shake_amount > 0:
        offset = Vector2(
            randf_range(-shake_amount, shake_amount),
            randf_range(-shake_amount, shake_amount)
        )
    else:
        offset = Vector2.ZERO
```

## Sprite Animation

### AnimatedSprite2D Controller
```gdscript
extends CharacterBody2D

@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D

func _physics_process(_delta: float) -> void:
    update_animation()
    update_facing()

func update_animation() -> void:
    if not is_on_floor():
        if velocity.y < 0:
            sprite.play("jump")
        else:
            sprite.play("fall")
    elif abs(velocity.x) > 10:
        sprite.play("run")
    else:
        sprite.play("idle")

func update_facing() -> void:
    if velocity.x > 0:
        sprite.flip_h = false
    elif velocity.x < 0:
        sprite.flip_h = true

func play_attack() -> void:
    sprite.play("attack")
    await sprite.animation_finished
    # Return to idle or movement animation
```

### Frame-Based Events
```gdscript
extends AnimatedSprite2D

signal attack_frame
signal step_frame

func _ready() -> void:
    frame_changed.connect(_on_frame_changed)

func _on_frame_changed() -> void:
    var current_anim := animation

    match current_anim:
        "attack":
            if frame == 3:  # Attack connects on frame 3
                attack_frame.emit()
        "run":
            if frame == 2 or frame == 6:  # Footstep frames
                step_frame.emit()
```

## Animation Tips

### Animation Speed Based on Movement
```gdscript
extends CharacterBody2D

@onready var anim_player: AnimationPlayer = $AnimationPlayer

@export var walk_speed := 100.0
@export var run_speed := 200.0

func _physics_process(_delta: float) -> void:
    var speed := velocity.length()

    if speed > 10:
        # Scale animation speed with movement speed
        var anim_speed := speed / walk_speed
        anim_player.speed_scale = clamp(anim_speed, 0.5, 2.0)
        anim_player.play("walk")
    else:
        anim_player.speed_scale = 1.0
        anim_player.play("idle")
```

### Root Motion
```gdscript
extends CharacterBody2D

@onready var anim_tree: AnimationTree = $AnimationTree

var root_motion_position := Vector2.ZERO

func _physics_process(delta: float) -> void:
    # Get root motion from animation
    var root_motion := anim_tree.get_root_motion_position()

    # Apply root motion as velocity
    if root_motion.length() > 0:
        velocity = Vector2(root_motion.x, root_motion.z) / delta
    else:
        # Normal movement when no root motion
        apply_movement_input()

    move_and_slide()
```

### Animation Retargeting
```gdscript
# When sharing animations between different skeletons
extends Skeleton3D

@export var source_skeleton: Skeleton3D
@export var bone_mapping: Dictionary  # {source_bone: target_bone}

func _process(_delta: float) -> void:
    if not source_skeleton:
        return

    for source_bone in bone_mapping:
        var target_bone: String = bone_mapping[source_bone]

        var source_idx := source_skeleton.find_bone(source_bone)
        var target_idx := find_bone(target_bone)

        if source_idx >= 0 and target_idx >= 0:
            var pose := source_skeleton.get_bone_pose(source_idx)
            set_bone_pose_rotation(target_idx, pose.basis.get_rotation_quaternion())
```

Related Skills

agent-memory-systems

16
from diegosouzapw/awesome-omni-skill

Memory is the cornerstone of intelligent agents. Without it, every interaction starts from zero. This skill covers the architecture of agent memory: short-term (context window), long-term (vector stores), and the cognitive architectures that organize them. Key insight: Memory isn't just storage - it's retrieval. A million stored facts mean nothing if you can't find the right one. Chunking, embedding, and retrieval strategies determine whether your agent remembers or forgets. The field is fragm

agent-embedded-systems

16
from diegosouzapw/awesome-omni-skill

Expert embedded systems engineer specializing in microcontroller programming, RTOS development, and hardware optimization. Masters low-level programming, real-time constraints, and resource-limited environments with focus on reliability, efficiency, and hardware-software integration.

agent-context-system

16
from diegosouzapw/awesome-omni-skill

A persistent local-only memory system for AI coding agents. Two files, one idea — AGENTS.md (committed, shared) + .agents.local.md (gitignored, personal). Agents read both at session start, update the scratchpad at session end, and promote stable patterns over time. Works across Claude Code, Cursor, Copilot, Windsurf. Subagent-ready. No plugins, no infrastructure, no background processes.

active-learning-system

16
from diegosouzapw/awesome-omni-skill

Эксперт active learning. Используй для ML с участием человека, uncertainty sampling, annotation workflows и labeling optimization.

33GOD System Expert

16
from diegosouzapw/awesome-omni-skill

Deep knowledge expert for the 33GOD agentic pipeline system, understands component relationships and suggests feature implementations based on actual codebase state

system-create-cli

16
from diegosouzapw/awesome-omni-skill

Generate production-quality TypeScript CLIs with full documentation, error handling, and best practices. Creates deterministic, type-safe command-line tools following PAI's CLI-First Architecture. USE WHEN user says "create a CLI", "build a command-line tool", "make a CLI for X", or requests CLI generation. (user)

animations-motion

16
from diegosouzapw/awesome-omni-skill

Add smooth animations to buttons, page transitions, tasks, modals, and interactive elements.

animation-skill

16
from diegosouzapw/awesome-omni-skill

Create splash screens, ASCII art banners, and terminal animations. Use when building visual effects, loading screens, and branding elements.

animation-rigging

16
from diegosouzapw/awesome-omni-skill

Character rigging skill for IK constraints.

email-systems

16
from diegosouzapw/awesome-omni-skill

Email has the highest ROI of any marketing channel. $36 for every $1 spent. Yet most startups treat it as an afterthought - bulk blasts, no personalization, landing in spam folders. This skill covers transactional email that works, marketing automation that converts, deliverability that reaches inboxes, and the infrastructure decisions that scale. Use when: keywords, file_patterns, code_patterns.

Argentine Invoice Processing System

16
from diegosouzapw/awesome-omni-skill

Complete invoice processing system for Argentine utility bills with OCR, classification, and automated organization

AI Nervous System - Document Intelligence

16
from diegosouzapw/awesome-omni-skill

Vector search and AI-powered document processing skills for OpenClaw integration