animation-tree is a skill from the Summer skill library: a guide your AI agent reads when the task matches, so it builds the good version on the first try. Wire AnimationTree state machines and blend trees: locomotion blends, attack interrupts, hit reactions.
When your agent uses it
- The character has clips that must play in response to gameplay — idle/walk/run blends, attacks that interrupt locomotion, hit reactions
- A character keeps T-posing
Install
summer setup installs every skill for your agent. To add only this one:
claude-code for your agent, such as codex or cursor. See Skills for every option.
The skill
This skill generates nothing. It composes existing clips on anAnimationPlayer into an AnimationTree with a StateMachine for distinct states (idle / attack / hit / dead) and BlendSpace nodes for continuous parameters (walk-run blend by speed). Wired wrong, you get T-poses, snapping, or clips that cut each other off.
The canonical Summer Engine stack:
AnimationPlayer.play() visibly moves the rig. The engine may import animation GLBs as AnimationLibrary resources or as PackedScenes with embedded AnimationPlayers. If a copied clip appears in the list but the mesh T-poses, its tracks probably target a different root/skeleton path. Use the embedded player that came with the matching GLB, or remap/copy tracks only after confirming they target the active Skeleton3D. An inactive AnimationTree is also a no-op, so always set AnimationTree.active = true.
When to use this skill
- Character has 3+ clips and switching between them via
AnimationPlayer.play("walk")is causing pops, T-poses, or “starts halfway through”. - Need a walk → run blend that’s continuous, not boolean.
- Need attack to interrupt locomotion but only on the upper body, while feet keep walking.
- Need hit-react one-shot that returns to whatever state was playing before.
- Building any non-trivial NPC or player character past the prototype phase.
When NOT to use this skill
- Single-clip looping background prop (banner waving, water surface). Just
AnimationPlayer.play("loop")is fine. - Cinematic with hand-authored timing. Use
AnimationPlayerdirectly withplay_with_capture; AnimationTree is overkill. - Character has zero clips — generate first via
generate-motion. - Procedural-only animation (no clips, all IK). That’s
procedural-animation.
Canonical state machine
This is the production-default for a humanoid character that can move, attack, take a hit, and die. Start here, prune what you don’t need, extend with named states for specials.- Locomotion is a single AnimationNodeBlendSpace1D parameterized by
move_speed(0..max). Three points on the line: idle (0), walk (~3), run (~6). Crossfades for free. - Attack is an AnimationNodeOneShot wrapping
attack_sword(or whichever clip). Fires from the script viaparameters/attack/request = ONE_SHOT_REQUEST_FIRE. Auto-returns to Locomotion when the clip ends. - Hit same pattern, higher priority — interrupts attack. Fires on
damagedsignal. - Dead is a transition-target with no exit. Trigger via
state_machine.travel("dead").
The fast lane: script the wiring (preferred)
Before walking the CRUD steps below, check the ctx lane — onesummer_run_script call replaces the add-node/set-prop chain AND the hand-written .tres:
- Animation-tier engines:
ctx.anim_state_machine(character, {states: {...}, transitions: [[from, to, {blend_s: 0.2}], ...], start: "idle"})gets-or-creates the AnimationTree, wires it to the player, builds the state machine, setsactive = true. Unknown clip names come back as a report entry listing the player’s REAL clips — never wire against guessed names. Full recipe (inspect clips/bones first, method-track events, root motion, playtest verification):character-animation-wiring. - Any engine (raw fallback): the same classes are fully script-bound —
AnimationNodeStateMachine.add_node(name, AnimationNodeAnimation),add_transition(from, to, AnimationNodeStateMachineTransition)(one transition resource each,xfade_timeon it), thentree.anim_player = tree.get_path_to(player),tree.tree_root = sm,tree.active = true, andctx.set_owner_recursive(tree). The raw script is quoted incharacter-animation-wiring.
anim_state_machine builds clip states + transitions only. The canonical machine below — BlendSpace1D locomotion, OneShot attack/hit — still needs the .tres (or raw-script) lane for those node types. Discrete idle/walk/run backbones don’t.
Steps via MCP
1. Inspect the current character
Skeleton3D and an AnimationPlayer with at least idle + walk + run populated. If clips are missing, route to generate-motion.
2. Add the AnimationTree
scenePath is a required argument on every scene-mutating tool
(summer_add_node, summer_set_prop, summer_set_resource_property,
summer_connect_signal, summer_save_scene, summer_instantiate_scene,
summer_remove_node, summer_replace_node). Omit it and the call is rejected
before it reaches the engine. Get the path from summer_get_project_context.
anim_player must be a relative NodePath; ../AnimationPlayer from a sibling. Don’t use absolute paths — breaks scene reuse.
3. Build the tree resource
Thetree_root is a single AnimationNodeStateMachine. Inside it, a Locomotion node which is itself an AnimationNodeBlendSpace1D, plus Attack, Hit, Dead nodes.
Three ways to build it, in preference order: (1) a summer_run_script GDScript
constructing the node graph via the script-bound API — transactional, no
hand-parsed text format (see the fast-lane section above; BlendSpace1D and
OneShot nodes are AnimationNodeBlendSpace1D.new() + add_blend_point(...)
and AnimationNodeOneShot.new() with fadein_time/fadeout_time, composed
with the same add_node/add_transition calls); (2) hand-writing the .tres
below and importing it; (3) summer_set_resource_property node-by-node — the
most verbose, use only for touch-ups. The .tres skeleton, for the read/write
fallback lane:
AnimationNodeStateMachineTransition carries xfade_time (0.15 default), switch_mode (Immediate / Sync / At End), and advance_mode (Auto / Enabled / Disabled).
Ask before writing the .tres: “May I write res://characters/goblin/animation_tree.tres with the canonical 4-state machine + Locomotion blendspace?“
4. Drive it from the controller script
tree.set(...) writes a parameter path. sm.travel(...) switches state.
5. Connect signals
emitter and receiver — not source/target, not
from/to. Those spellings fail schema validation.
If the goblin has an HP component emitting died, connect that to die.
6. Save & verify
summer_get_script_errors checks one file and requires path; there is no
“check everything” form. For a project-wide sweep use summer_get_diagnostics.
Then summer_play and watch — the goblin should idle in place, blend up to walk/run as it moves, fire attack on input/AI, snap to hit on damage, end in Dead.
Confirmation gates
- Before writing the
.tres: show the state list and transition table. - Before connecting signals: show source → target → method.
- Before pressing play: state which behaviors should be visible (idle plays, walk crossfades, attack interrupts).
Reference card
Parameter paths cheat-sheet
Transition switch_mode decision
- Immediate (0): snap to new state at xfade. Use for hit reactions, deaths.
- Sync (1): match current play position. Use for walk ↔ run (avoids foot-pop mid-stride).
- At End (2): finish current clip first. Use for attack-string combos (
attack_1→attack_2only at end ofattack_1).
Pitfalls
- Character T-poses on play.
AnimationTree.active = truewas missed. Set it in_ready()or via inspector. - Locomotion blendspace stuck on idle even when moving. The
blend_positionparameter isn’t being written — confirmtree.set("parameters/Locomotion/blend_position", speed)runs every frame in_physics_process. Also confirm the path matches your state name exactly (case-sensitive). - Walk-to-run pops. Transition
switch_modeisImmediate. Set toSyncso the cycle phase matches across the crossfade. - Attack one-shot fires every frame as long as input is held. Use
Input.is_action_just_pressed, notis_action_pressed. Or store a_can_attackflag with cooldown. - Hit reaction interrupts itself when hit twice in 100ms. Set
fadein_time = 0.0and gate from script:if sm.get_current_node() == "Hit": return. - Attack plays but locomotion is invisible (lower body T-pose). OneShot’s
mix_modeisBLEND(full body). For upper-body-only attacks, setmix_mode = ADDand use an additive clip, or drive the lower body from a secondAnimationTreebranch and blend by bone with anAnimationNodeBlend2plus filters.SkeletonModification3Dis an old upstream class name andClassDB.class_exists("SkeletonModification3D")returns false on the current Summer build. - Dead state replays from start every time HP < 0. Add a guard in the controller:
if is_dead: return.
One-shot vs StateMachine — which to use
- OneShot: the action is a temporary override of an underlying state (attack while running, hit-react while attacking). The base keeps playing.
- StateMachine state: the action is a replacement (dead, stunned, ragdoll). Nothing else plays.
- Action combat is usually OneShot for attacks + a StateMachine state for “dead”. Don’t model attacks as StateMachine states unless you need precise transition control between attack-string variants.
BlendSpace1D vs BlendSpace2D
- 1D for speed-only locomotion (idle / walk / run).
- 2D for omnidirectional movement (idle / forward / strafe-left / strafe-right / back). Eight or nine clips arranged on a unit circle. Necessary for top-down or strafing third-person; overkill for FPS hands.
Anti-patterns
- Skipping AnimationTree and calling
AnimationPlayer.play("walk")from script. Works for one clip; breaks the moment you need a crossfade. - Modeling every attack as a StateMachine state. The transition matrix explodes (N states × N transitions). Use one OneShot per attack and switch its inner clip via
parameters/Attack/animation. - Writing the
.tresby hand without using sub_resource IDs — the engine loses the references. Always use[sub_resource id="..."]blocks. - Driving
blend_positionfrom raw input (Input.get_axis(...)). Drive from actual velocity — that way the blend matches the body’s real motion, including knockback and slope-slide.
Edge cases
- Player rotates 180° instantly while sprinting. The blendspace doesn’t know about turning — locomotion looks like the character moonwalks for one frame. Add a small additive turn-lean clip layered on top, or add a brief “turn” state with switch_mode=AtEnd.
- Animation library uses root motion. Set
root_motion_trackon the AnimationTree (path to the root bone), then in_physics_processusetree.get_root_motion_position()and apply to the CharacterBody3D. Don’t combine with input-driven velocity — pick one. - Character has no AnimationPlayer. Add one first. AnimationTree without anim_player target is a no-op.
Fallback (no MCP)
Open the scene in Summer Engine, add anAnimationTree node, set Anim Player
to the AnimationPlayer, set Active = on, double-click Tree Root, and edit
visually. Drag AnimationNodeStateMachine as the root, drop nodes inside, draw
transitions by Shift-dragging, and save the scene. The visual editor produces
the same .tres; it is slower but discoverable.
Handoff
- For NPC behavior firing these states (decide-when-to-attack), hand off to
design-npc. - For first-person hand animations on the player rig, the same pattern applies — hand off to
fps-controllerfor the state inputs. - For procedural overlays on top of the tree (look-at the player while idling), hand off to
procedural-animation. - For lipsync layered on the face during dialogue states, hand off to
facial-and-lipsync.
See also
character-animation-wiring— the end-to-end rigged-GLB path: inspect real clip/bone names,ctx.anim_state_machine, method tracks, root motion, playtest verification.generate-motion— produce the clips this tree references.retarget— share one tree across many characters.
From a shell
No Summer MCP tools in this session? Run each one as a command; whensummer --version is missing or below 4.0, use npx -y summer-engine@latest in place of summer. Fields are flags in kebab-case (--scene-path res://main.tscn) or one --input '<json>', and summer ops describe <op> prints the exact input. The summer-cli skill has the rest.
Related skills
character-animation-wiring: Wire a rigged character end to end: real clips and bones, locomotion states, method tracks, root motion.design-npc: Design enemy, NPC, boss or companion behavior: perception, state machine, telegraphs, script stub.facial-and-lipsync: Make a character’s mouth move with a voice-over: phonemes, viseme blendshapes, facial expressions.generate-motion: Attach a library animation clip (idle, walk, run, attack) to a rigged character via AnimationPlayer.procedural-animation: Runtime bone changes on top of clips: head look-at, foot IK, hand grabs a prop, lean, recoil.retarget: Apply animation clips from one rigged character to another: one clip library, many models.fps-controller: First-person controller: WASD, mouse look, jump, coyote time, jump buffering, air control.
Source:
library/skills/animation-tree/SKILL.md, rendered from summer-engine@4.3.0 on npm.
