scenario-godot-animation
About scenario-godot-animation
A Godot 4.7.2 animation skill for animators and technical animators. It combines code-driven setup with measured motion checks, pose sheets, and explicit validation of imports, animation trees, layers, and IK.
Key features
- 01Audit imported clips and retargeted rigs
- 02Measure foot speed for locomotion
- 03Build and drive animation trees in code
- 04Set up filtered layers and IK
- 05Inspect fixed-time poses with contact sheets
- 06Validate cutscenes and camera behavior
Use cases
- 01Match locomotion speed to planted-foot motion
- 02Retarget clips to a humanoid skeleton
- 03Add upper-body actions and foot placement
- 04Check a cutscene timeline and camera checkpoints
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ scenario-labs/ skills --skill scenario-godot-animationAfter installing, type/scenario-godot-animationin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/critique.mdexpert-notes.mdgui-paths.mdprocedures.mdsources.mdscripts/gd_anim.pyscripts/agentkit/animation/anim_audit.gdanim_capture.gdanim_cutscene.gdanim_ik.gdanim_import.gdanim_motion.gdanim_rig.gdanim_setup.gdanim_tree.gdanim_tween.gdcutscene_director.gdfoot_placer.gdlocomotion_state.gdpose_persist.gdDetails
From the project READMEGodot animation (animator and technical animator)
Target: Godot 4.7.2.stable, macOS, Forward+ with Jolt Physics. Every procedure in
references/procedures.md was run live in 4.7.2 on 2026-10-02 against a
procedurally built humanoid, so nothing needs a download.
Stance
- Measure motion, do not eyeball it. A walk has one correct ground speed: the speed at which the
planted foot stops sliding. Measure it from the clip (
anim_motion.gd:foot_speed) and use that number for BlendSpace points and the controller. On a retargeted body the speed scales withSkeleton3D.motion_scale(a 1.15x body walked 1.38 m/s on a 1.2 m/s clip). - Code decides, the animation layer presents. Gameplay owns velocity and state; the AnimationTree only reads them through advance expressions or parameters. The controller must still work with the tree removed (Fair Fight, jzuvd0Lstuw 00:05:46).
- Look at exact times. Seek or step to fixed times and open a contact sheet
(
gd_anim.pose_sheet). Real-time captures land on random frames. - A layer must say which bones it owns. Partial clips do not stay partial: glTF export pads every clip with every animated joint, and an unfiltered OneShot under the default deterministic blending snaps every unlisted bone to RESET. Filters, not missing tracks, define layers.
- Choose the driver per motion. Velocity-driven locomotion with in-place clips keeps control responsive; root motion suits one-shot moves (dodge, climb, vault) where the feet must stay planted [added]. A tree suits a normal locomotion set; very large move sets or many procedural layers favor code plus SkeletonModifier3D layers (the experts disagree: Fair Fight against trees, xpoPfUKI9tw; RiftOfNostaria, Bitlytic and Chris for them). Tweens for runtime values with unknown starts, AnimationPlayer for authored multi-property curves (Queble, KUyQzjpRsU8 00:01:48).
- Silent failures are the norm here. A BoneMap naming bones that do not exist, an expression
transition at advance mode Enabled, a missing RESET key: each produced no error in 4.7.2. Audit
after every import and every tree change. If a sibling skill named here is missing from your
available skills, ask the user to install it
(
npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.
Establish first
Ask once, then run:
- Source of motion: Mixamo or other FBX, Blender glTF, mocap, hand-keyed in Godot, procedural only.
- Rig: humanoid (SkeletonProfileHumanoid fits) or custom; T-pose or A-pose rest; real Root bone or not.
- Locomotion model: velocity-driven (in-place clips) or root motion; target speeds if the design fixes them.
- Layers needed: upper-body actions, aim or look-at, foot IK on uneven ground, hit reactions.
- Characters sharing clips, and their height range.
- Cutscenes: length, camera count, whether gameplay must be frozen, dialogue system in use.
- Physics tick, renderer and target hardware (for IK and modifier cost).
Workflow with gates
-
Kit and project.
gd_anim.install_animkit(P); confirmproject.godothas Jolt for 3D and the brief's renderer. Gate: the kit's jobs returnok. -
Import. Name cycles with a loop hint (
Walk-loop), import once, then set options in the.importfile (gd_anim.set_import_options, P3) and run--import. Turn onanimation/import_rest_as_RESET. Gate:gd_anim.audithas no flags; every cycle loops; RESET exists. -
Retarget. Build the BoneMap from the imported bone names (
anim_import.gd:make_bonemap, P4);:becomes_on import. Gate: skeleton is%GeneralSkeleton, bones renamed, sampled poses within 2 mm of the source. -
Share. Import clips As Animation Library, give each body its own BoneMap, attach with
anim_setup.gd:share_library(P5). Gate: 0 unresolved tracks, identical track layout. -
Speeds.
foot_speedper cycle and per body (P9). Gate: controller and BlendSpace speeds within 2 percent. -
Tree. Build in code (
anim_tree.gd, P6): BlendTree root, StateMachine inside, BlendSpace1D with sync, OneShot with a filter, TimeScale. Gate:drivewith scripted input gives the expected state list and times; tree audit clean. The transition helper that passed the drive test:static func transition(expr: String, at_end: bool, xfade: float) -> AnimationNodeStateMachineTransition: var t := AnimationNodeStateMachineTransition.new() t.xfade_time = xfade t.advance_mode = AnimationNodeStateMachineTransition.ADVANCE_MODE_AUTO # default Enabled never auto-fires if at_end: t.switch_mode = AnimationNodeStateMachineTransition.SWITCH_MODE_AT_END if expr != "": t.advance_expression = expr # read on tree.advance_expression_base_node return t # sm.add_transition(&"Ground", &"Fall", transition("not on_floor and not jump_pressed", false, 0.2))Drive it from gameplay with
tree.set("parameters/sm/Ground/blend_position", speed)and the state node's properties; fire actions withtree.set("parameters/act/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE). -
Layers and IK. OneShot filters (P7), FootPlacer plus TwoBoneIK3D (P10). Gate: foot within 2 cm of the target on a step, a hole and a 15 deg slope, read in
skeleton_updated. Modifiers run in child order, so the placer (targets and hips) goes before the solver:var ik := TwoBoneIK3D.new() skeleton.add_child(ik) # after FootPlacer ik.setting_count = 2 for i in 2: var side: String = ["Left", "Right"][i] # := cannot infer from an array index ik.set_root_bone_name(i, side + "UpperLeg") ik.set_middle_bone_name(i, side + "LowerLeg") ik.set_end_bone_name(i, side + "Foot") ik.set_target_node(i, ik.get_path_to(targets[i])) ik.set_pole_node(i, ik.get_path_to(knee_poles[i])) # 0.6 m in front of the knee ik.set_pole_direction(i, SkeletonModifier3D.SECONDARY_DIRECTION_PLUS_Z) skeleton.skeleton_updated.connect(_check_feet) # final pose, after every modifier and influenceLower
influencewhile running and set it to 0 in the air, so authored motion shows through (Lukky, MbaPDWfbNLo 00:09:30). -
Camera and cutscene. SpringArm3D with a sphere shape (P12); one AnimationPlayer timeline (P13). Gate: call order and times, camera at checkpoints,
animation_finished, section playback. -
Look. Pose sheets: T-pose front and side, 8 walk phases, each state, IK terrain, cutscene timeline (P14). Gate: every sheet opened and described.
Expert checklist (state these in the plan)
- Modifier output is rolled back every frame (Fair Fight XFZQNsFejwk 00:02:04; P10 reads the
animated pose a frame later). Fine for post effects; when modifiers own the pose, put
pose_persist.gdwith role RESTORE first and SNAPSHOT last: an incremental 5 deg modifier then reached 60 deg in 12 frames instead of staying at 5 (P16). RESTORE overrides the animation, so never use it on top of a playing clip. - IK solves position only. TwoBoneIK3D left the foot 12.1 deg off a 15 deg slope; a
CopyTransformModifier3Dafter it (rotation only, reference type Node, an align node with the ground tilt) brought it to 0.0 deg without moving the foot (P16; Lukky MbaPDWfbNLo 00:07:49). - Deterministic IK solves from scratch each frame; a one-iteration chain needs the angle limit
at the full 180 deg (Lukky 00:04:46).
deterministicdefaults to false on IterateIK3D; turn it on for stepping legs (MeroDev 17xi4vDqQJk 00:02:28). - Bezier and property tracks cannot blend (docs): the tree blends value, position, rotation and scale tracks.
- Call Method tracks do not run in the editor preview (docs): test them in a run (P13).
- Capture update mode blends from the current value to the first key: a property at 5 with a key
10 at 0.5 s read 7.5 at 0.25 s under
play_with_capture(P16). - Cache
find_trackin a path-to-index map when scripts sample many tracks: Fair Fight measured 1.5 to 3x on large rigs (XFZQNsFejwk 00:04:03 to 00:06:45); on the 22-track test rig in GDScript, 1.2x (P16). It needs one track layout across clips, so turn offanimation/remove_immutable_tracks. - Gate one-shots on
animation_finishedplus a flag, or the next frame's locomotion call replaces the hit clip (DevWorm GMyw3eHDw7s 00:22:58). Measured: "hurt" held for its 0.4 s, then walk resumed (P16). - Blender source. Rigify Limb Segments 1 and Game Friendly single root before export (CoderNunk
0wMONEpEXpQ 00:00:19, 00:01:23).
nodes/apply_root_scale(default true) bakes import scale into meshes and clips, root stays at 1 (docs). - BlendSpace2D for 4-way sprites: points at plus or minus 1, axis range plus or minus 1.1 so diagonals snap to left and right; 2D up is negative Y (Chris WrMORzl3g1U 00:09:20, 00:13:09).
Numbers
| What | Value | Source |
|---|---|---|
| Human walk, run ground speed | about 1.2 to 1.5 m/s walk, 3 to 4 m/s jog [added] | measure the clip instead (P9) |
| No-slide speed measured | walk 1.200, run 3.504 m/s on clips built for 1.2 and 3.5 | P9 |
| Foot-slide tolerance | planted foot under 0.1 m/s; speeds within 2 percent [added] | baseline G5, P9 |
| Locomotion crossfade | 0.1 to 0.2 s; landing 0.05 s; Land to Ground at end with 0.15 s | P6 |
| Import FPS | 30 (default animation/fps) | importer |
motion_scale after Normalize Position Tracks | source hip height (0.97 m test rig) | P4 |
| IK foot error | under 2 cm required, under 0.1 mm measured | P10 |
| FootPlacer ray | 0.5 m above to 1.0 m below the animated foot; hips drop clamp 0.35 m | P10 |
| Spring arm | sphere r 0.2 stops 0.2 m plus margin 0.01 m short of a wall | P12 |
| IterateIK3D defaults | max_iterations 4, min_distance 0.001, angular_delta_limit 2 deg, deterministic false | class reference |
| Tween stagger | 0.05 s per item, 0.2 s per tween | P11; aagPe4dvM2M 00:04:04 |
| Modifier cost | Fair Fight halved a 2 ms overlay with incremental global transforms | XFZQNsFejwk 00:14:07 |
Quality gates
Score with references/critique.md; record each live check as "run in
Godot 4.7.2 on : pass, key numbers" and mark anything not run "not yet run" with the reason.
Blocking: every track resolves; cycles loop and RESET covers every animated path; speeds match the
measured no-slide speeds; the tree passes a scripted drive; a pose sheet was opened. Also: no
overwrite_axis or other keys missing from 4.7.2 in .import edits; no SkeletonIK3D in new code;
IK results read in skeleton_updated or modification_processed, never from _process.
Common mistakes
-
BoneMap with
mixamorig:names. Godot importsmixamorig_Hips; the map matches nothing, the skeleton is still renamed GeneralSkeleton, every library track fails to resolve, and nothing is printed. Build the map from imported names; the audit flags a GeneralSkeleton with no Hips. -
Expression transitions left at advance mode Enabled. They never fire. Set
ADVANCE_MODE_AUTO(2) on every transition driven by an expression or condition. -
Two auto transitions true in one frame. A jump that clears
on_floorwent Ground to Fall, skipping JumpUp. Guard the expression (not on_floor and not jump_pressed) or give the jump transition priority 0. -
Unfiltered OneShot or Add with a partial clip. Deterministic (the AnimationTree default): unlisted bones go to RESET, the character T-poses. Non-deterministic: they freeze. Filter the node.
-
No RESET. Imported files have none by default; blends and saved scenes then start from rest or zero.
animation/import_rest_as_RESET, or author RESET upstream (Blargis, ghYilg9cq-I 00:02:39). -
Reading IK results in
_process. You get the animated pose. Read inSkeleton3D.skeleton_updated(final, after influence) or in the modifier'smodification_processed(100 percent, before influence). -
Root motion on Hips. It wobbles; insert a real Root (FinePointCGI, fq0hR2tIsRk 00:04:35). Velocity is
basis * get_root_motion_position() / delta. -
Speed constants on retargeted bodies. A taller body has a longer stride through
motion_scale; re-measure per body. -
Tweens without a kill guard. Two tweens on one property both write every frame (Queble, KUyQzjpRsU8 00:02:54). Keep a reference and
kill()it. -
Method tracks in a headless or skipped cutscene. Default Deferred calls wait for a frame; a loop that never yields fires them all at the end. Use Immediate, or yield frames.
-
Editing tree nodes at runtime. They are shared by every instance of the scene; per-character values belong in
parameters/.... -
Ray-only spring arm. The camera lands on the wall plane; give the arm a sphere shape.
-
Cameras keyed without a look target. A cutscene camera whose rotation keys drift lost the hero for the last 5 s; check framing on the timeline sheet, not on the first and last key.
-
Library loaded on a body with its own clips. Skip Import on the second character's AnimationPlayer so only the shared library plays (Quilled, nb6uSeEZFCI 00:04:36).
Handoffs
- scenario-godot-expert: run protocol,
gd_run, capture and review tools. Read it first. - scenario-godot-gameplay: the CharacterBody3D controller and the state the tree reads; hitboxes from method tracks.
- scenario-godot-3d-world: collision layers for FootPlacer and spring arm; IK test geometry.
- scenario-godot-pipeline-automation: batch import and
.importedits at scale, CI runs. - scenario-godot-ui, scenario-godot-vfx, scenario-godot-audio: menu motion beyond tweens; effects and sounds keyed from clips and cutscenes.
- scenario-godot-performance-export: modifier and tree cost on target hardware.
- blender- skills:* Root bone, Rigify Game Friendly export, NLA push-down, loop hints at the source.
Godot 4.7 notes
- IK family since 4.6 on SkeletonModifier3D: TwoBoneIK3D, FABRIK3D, CCDIK3D, JacobianIK3D,
SplineIK3D; settings are per index (
set_root_bone_name(i, ...),set_target_node(i, path),set_pole_node,set_pole_direction). SkeletonIK3D is deprecated. - Custom modifiers override
_process_modification_with_delta(delta);_process_modification()is deprecated. Do not applyinfluenceyourself. retarget/rest_fixer/retarget_method(0 None, 1 Overwrite Axis default, 2 Use Retarget Modifier) replaces anyoverwrite_axiskey; RetargetModifier3D exists for runtime retargeting (not run here).tween_await(signal).set_timeout(s)is new in 4.7;tween_subtweensince 4.4.- Animation markers and
play_section_with_markers(4.3+);Animation.lengthis a double in 4.7; animation names are StringName. - AnimationMixer is the base of AnimationPlayer and AnimationTree:
root_motion_track,deterministic(false on the player, true on the tree),callback_mode_process,callback_mode_method. - Observed in 4.7.2 (P6):
priority(lower wins) also decides between auto transitions that are true in the same frame, not onlytravel()paths;travel()called before the firstadvance()starts the machine in that state without error; a transition's defaultadvance_modeis Enabled (travel only). - Observed (P10):
modification_processedreports the modifier's result beforeinfluence;skeleton_updatedgives the blended final pose; outside both, bone poses read as animated. - Observed (P3): a hand-edited
.importis applied by a plain--import; loop-hint suffixes are stripped from clip names, aloopprefix is kept;.in clip names becomes_. - Skeleton3D refuses
:and/in bone names; its error string for that is malformed in 4.7.2 (logged intests/OPEN_ISSUES.md).
References
references/procedures.md(P1 to P16, live results, not yet run),expert-notes.md(attributed claims),critique.md(rubric, scored G5 baseline),gui-paths.md(editor clicks),sources.md.scripts/gd_anim.py: Python helpers (install, run, rig, pose sheets, audit, GLB edits,.importedits, bone sampling).scripts/agentkit/animation/:anim_rig,anim_audit,anim_capture,anim_import,anim_setup,anim_tree,anim_motion,anim_ik,anim_tween,anim_cutscene, plusfoot_placer.gd,locomotion_state.gd,cutscene_director.gd,pose_persist.gd.- Tests:
tests/code/godot-animation/test_anim_live.py(A1 to A9, W) andtest_anim_offline.py.