Animation graphs
An animation graph is a state machine over a model’s clips. It has parameters, states that play one clip or blend several, transitions with conditions and cross-fades, and timed events that play sounds and notify scripts. The same graph can also carry IK, root motion and montages; see IK & retargeting.
Graphs are stored in the project and can drive placed models, spawned characters and the third-person avatar.
Create a graph
Section titled “Create a graph”- Open Window → Animation graphs.
- Type an ID in the new graph id field and click Add graph. The graph starts with one state,
idle. - Under Clip library, pick the model that contains the clips. Leave it empty to use the clips of whichever model the graph runs on.
- Add Parameters, States and Transitions.
- Click Apply. Revert discards your edits; Delete graph removes a graph that nothing uses.
The panel validates the graph as you edit and shows the first problem above the Apply button.
Use a graph
Section titled “Use a graph”| Character | Where to assign the graph |
|---|---|
| A placed model | Select it, add the Animation component with Add Component, then set Graph in its Animation section |
| The third-person avatar | World & terrain → Player → Animation graph |
| Any character, from a script | anim_graph(id, "graph_id") |
Without a graph, a model’s Animation component loops its single Clip at the set Speed.
Example: an idle, walk and run character
Section titled “Example: an idle, walk and run character”You will give the player avatar in the War City example a graph that blends idle, walk and run
by speed, and switches to a falling pose in the air. War City’s avatar, Character_Soldier, has the
clips Idle, Walk, Run and Jump_Idle. (In a source checkout, War City’s models are downloaded
by its build.py; run it once first.)
- Choose File → Open example → War City, then File → Save project to keep your own copy.
- Open Window → Animation graphs. Type
soldierin new graph id and click Add graph. - Set Clip library to Character_Soldier, so the clip fields list its clips.
- Under States, change the first state’s Id to
moveand click Make blend space. Blend by is alreadyspeed. Set the two points toIdleat0andWalkat2.4, then click + Blend point and addRunat5.2. These match War City’s walk and run speeds in m/s. - Click + State, set its Id to
falland its Clip toJump_Idle. - Under Transitions, click + Transition. Set it from
movetofall, click + Condition and choosegroundedFalse, with a0.15 s fade. Add a second transition fromfalltomovewhengroundedis True, with a0.2 s fade. - Set Initial state to
moveand click Apply. - Open Window → World & terrain, select the Player tab and set Animation graph to soldier.
speed, grounded and vertical_speed are filled in by the engine for the avatar, so the graph
does not declare any parameters.
Do the same from the command line, on a copy of the example (with its assets/ folder). Save the
graph as soldier.json:
{"value": { "id": "soldier", "name": "Soldier locomotion", "library_asset_id": 8890572774061676, "states": [ {"id": "move", "blend_param": "speed", "blend": [{"clip": "Idle", "at": 0}, {"clip": "Walk", "at": 2.4}, {"clip": "Run", "at": 5.2}]}, {"id": "fall", "clip": "Jump_Idle"} ], "transitions": [ {"from": "move", "to": "fall", "conditions": [{"param": "grounded", "op": "False", "value": 0}], "duration": 0.15}, {"from": "fall", "to": "move", "conditions": [{"param": "grounded", "op": "True", "value": 0}], "duration": 0.2} ], "initial": "move"}}cp -R examples/war-city /tmp/demoP="--workspace /tmp/demo --project project.feather.json"
feather-cli $P command animation.clips --json '{"asset_id":8890572774061676}' # Idle, Walk, Run, Jump_Idle…feather-cli $P command animation.upsert --json "$(cat soldier.json)" --save project.feather.jsonfeather-cli $P command project.player --json '{"animation_graph":"soldier"}' --save project.feather.jsonCheck it worked. Press Play. Standing still plays Idle; hold Ctrl to walk and the
avatar blends into Walk; move normally and it reaches Run; jump and it switches to Jump_Idle
until it lands. From the command line, animation.get with {"id":"soldier"} returns both states,
the three blend points and the two transitions, and animation.get with no arguments lists
soldier with "states": ["move", "fall"]. game.simulate does not evaluate animation, so check the
blending itself in Play.
Common problems
Section titled “Common problems”- A state does not play its clip. Clip names are not checked when you apply, so a misspelt
clip is accepted. Compare every name with
animation.clips. The engine’s default namesIdle_Loop,Walk_LoopandJog_Fwd_Loopare the exception: on a model without them they play its conventionally named idle, walk and run clips; see Locomotion clips. - Apply reports an unknown parameter. Every condition and Blend by must name a declared
parameter, or one of
speed,groundedandvertical_speed. - A graph cannot be deleted. Something still uses it, such as Player → Animation graph. Clear that first.
Parameters
Section titled “Parameters”Parameters are the inputs that transitions test and blend spaces read.
| Kind | Value | Notes |
|---|---|---|
| Float | A number | Set with anim_set(id, name, number) |
| Bool | 0 or 1 | Set with anim_set(id, name, true) |
| Trigger | Set or not set | Fired with anim_trigger(id, name); consumed by the transition that tests it |
Each parameter has a starting value. The engine fills in three parameters for the third-person avatar every frame, so you can use them without declaring them:
| Parameter | Value |
|---|---|
speed |
Movement speed |
grounded |
1 on the ground, 0 in the air |
vertical_speed |
Vertical velocity |
States
Section titled “States”A state plays a single clip, or a 1D blend space that mixes clips along a parameter.
| Setting | Description | Default |
|---|---|---|
| Clip | The clip to play | — |
| Blend points | Clips placed at values along the Blend by parameter. Click Make blend space to convert a state, Single clip instead to revert | None |
| Speed | Playback rate, 0 to 10 | 1 |
| Speed parameter | Multiplies the speed by a parameter’s value | None |
| Loop | Whether the clip loops | On |
| Events | Timed events in the clip; see below | None |
Initial state sets the state the graph starts in. When it is empty, the first state is used.
Transitions
Section titled “Transitions”A transition moves from one state, or from any state (*), to another.
| Setting | Description | Default |
|---|---|---|
| From | A state ID, or * for any state |
* |
| To | The target state | — |
| Conditions | All must hold. Each compares a parameter with Greater, Less, GreaterOrEqual, LessOrEqual, Equal or NotEqual, or tests True / False | None |
| Duration | Cross-fade time in seconds, 0 to 10 | 0.2 |
| Exit time | Waits until the source clip reaches this normalized time, 0 to 1. Empty means any time | Empty |
Transitions are checked in order each frame; the first one whose conditions hold fires.
Events
Section titled “Events”Events fire at a normalized point of a state’s clip, from 0 to 1. Each event can play a project sound
at the character, at a set volume, and calls the on_anim_event(id, name) hook in world scripts.
Use them for footsteps, weapon swings and hit frames.
// World script.fn on_anim_event(id, name) { if name == "footstep" { print("Step from " + id); }}Script API
Section titled “Script API”The id argument is the character’s object ID. Pass "player" for the third-person avatar.
| Function | Description |
|---|---|
anim_graph(id, graph) |
Runs a graph on a character; an empty name removes it |
anim_set(id, name, value) |
Sets a Float or Bool parameter |
anim_trigger(id, name) |
Fires a Trigger parameter |
anim_state(id) |
Returns the current state ID |
play_montage(id, clip), play_montage(id, clip, options) |
Plays a one-shot over the graph |
stop_montage(id), stop_montage(id, blend) |
Stops the playing montage |
montage_playing(id) |
Returns the playing montage, or an empty string |
Montages, look-at and IK weights are covered in IK & retargeting.
fn update(dt) { if key_down("E") { anim_trigger("player", "wave"); }}Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Graphs per project | 128 |
| Graph, state, parameter and montage IDs | 1 to 64 letters, digits, - or _ |
| Blend points per state | 16 |
| Events per state | 32 |
| Event names | 64 characters |
| Montages per graph | 64 |
Automate it
Section titled “Automate it”| Command | Purpose |
|---|---|
animation.get |
Lists graphs, or returns one in full with id |
animation.upsert |
Creates or patches a graph (JSON merge; arrays replace). rename_from renames it and updates its users |
animation.remove |
Deletes a graph that nothing uses |
animation.clips |
Lists a model’s clips and their lengths |
Create a locomotion graph that blends idle, walk and run by speed and jumps on a trigger:
feather-cli --connect .feather-session.json command animation.upsert --json '{ "value": { "id": "hero", "name": "Hero", "parameters": [{"name": "jump", "kind": "Trigger", "default": 0}], "states": [ {"id": "move", "blend_param": "speed", "blend": [{"clip": "Idle", "at": 0}, {"clip": "Walk", "at": 2}, {"clip": "Run", "at": 6}]}, {"id": "jump", "clip": "Jump", "looping": false} ], "transitions": [ {"from": "move", "to": "jump", "conditions": [{"param": "jump", "op": "True", "value": 0}], "duration": 0.1}, {"from": "jump", "to": "move", "exit_time": 0.9, "duration": 0.2} ], "initial": "move" }}'Clip names must match the model’s clips; list them with animation.clips. See
Agents & automation for connecting a live session.