> For the complete documentation index, see [llms.txt](https://studio-docs.sandbox.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://studio-docs.sandbox.game/the-editor/create-an-animation-state-machine.md).

# Create an Animation State Machine

An animation state machine decides **which clip plays now** and **when to switch**. You do not play clips by name from gameplay every frame. You set **parameters**. The graph compares those values and takes **transitions**. The mesh follows the active **state**.

This page is for a person building or **debugging** the graph in the Model Viewer, and for an agent that must wire the same system in a v14 project. Read how the config and graph fit together before you click through the stepper. When motion fails, open the config first — do not burn a long chat guessing in TypeScript.

{% embed url="<https://youtu.be/WKH3x1KFaK0?si=M6TuBA9RO4AZ1rkB>" %}

## Animation config, graph, and state machine

Three names for related pieces. Mixing them up is the usual failure.

| Name                   | What it is                                                                       | Where it lives                                                                   |
| ---------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **Animation config**   | The asset that stores the whole graph                                            | `assets/.../*.animconfig.json` (`ENGINE.AnimationConfigResource`)                |
| **Graph**              | What you see in the Model Viewer: **states** (boxes) and **transitions** (lines) | Edited inside that config                                                        |
| **State machine node** | The scene node that **plays** the config on a skeleton                           | `ENGINE.AnimationStateMachineNode` on the character; Inspector field `configUrl` |

The `.glb` holds recorded **clips** (Idle, Run, an attack). The **config** chooses which clips matter, how they loop, which state plays which clip, and which lines connect those states. The **node** only points at the config and receives parameter writes from the pawn or your code.

```
.glb clips  →  .animconfig.json (graph)  →  AnimationStateMachineNode.configUrl  →  mesh moves
                      ↑
              parameters from pawn / setParameter
```

If the clip is only in the `.glb` and never enters the config, the graph cannot play it. If two states exist but there is **no transition line**, a parameter change will not move you between them. If `configUrl` is empty or points at the wrong file, Play uses nothing useful.

{% hint style="info" %}
*NOTE: Configs are `.animconfig.json`. Projects from before engine 13.2 may still have `.anim.json`; convert those to `.animconfig.json` before this workflow.*
{% endhint %}

### What the graph is doing

A **state** is one mode of motion: Idle, Run, Jump, an attack. Only one state is active on a layer at a time. The config marks one state as **`initialState`** — that is where Play starts.

A **clip** is one recorded animation. A state either plays a **single clip**, or **blends** several clips using Number parameters as weights (for example forward / back / left / right).

A **transition** is a **line** on the graph from one state to another. It has a destination, a `blendDuration` in seconds, and a **condition**. When the condition is true, the machine leaves the current state and mixes into the next.

A **parameter** is a Boolean or Number stored on the config and updated at runtime. The graph only **reads** parameters. Gameplay code (or the default Character Pawn) **writes** them with `setParameter`. If nothing writes parameters, the character stays in `initialState` forever.

Typical locomotion loop:

1. Pawn moves → code sets `isRunning: true` and a `forward` weight.
2. The Idle → Run transition’s condition matches.
3. The machine blends into Run.
4. When movement stops, the return transition’s condition sends the machine back to Idle.

Conditions you will see:

| Condition                | Meaning                                                            |
| ------------------------ | ------------------------------------------------------------------ |
| Boolean / Number compare | Equal, not equal, greater than, less than a value you chose        |
| `clipFinished: true`     | The current clip reached the end — used for attacks and recoveries |

### What sits inside `.animconfig.json`

The Model Viewer edits this file. Prefer the graph UI. Knowing the parts helps you diagnose and brief the agent.

| Part                       | Role                                                                                        |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `initialState`             | Name of the state that starts on Play                                                       |
| `parameters`               | Default Booleans and Numbers (for example `isRunning`, `forward`)                           |
| `states`                   | Each named state: a `clip`, or a `blend` list, plus `transitions` to other states           |
| `transitions` (on a state) | Lines out of that state: `to`, `blendDuration`, `condition`                                 |
| `clipOptions`              | Per-clip settings such as loop (`LoopRepeat` vs `LoopOnce`)                                 |
| `oneShotClips`             | Clips marked to play once over the current state (often empty when attacks are full states) |
| `externalAnimationSources` | Extra `.glb` files (and optional skeleton profiles) that supply more clips                  |

A state with no `transitions` array (or an empty one) never leaves that state by graph rules. Death poses sometimes stay that way on purpose. Locomotion Idle ↔ Run **must** have lines both ways (or you will stick).

### One-shot vs looping vs attack states

| Setup                                                                    | Behaviour                                                                     | Use for                                          |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------ |
| Clip / state set to **loop** (`LoopRepeat`)                              | Motion repeats until a transition leaves the state                            | Idle, walk, run                                  |
| Clip set to **play once** (`LoopOnce`)                                   | Plays to the end, then stops or holds                                         | Attacks, hit reacts, death                       |
| **One-shot clips** list on the config                                    | Play once **over** the current state without permanently replacing locomotion | Short overlays — easy to misread when diagnosing |
| Attack as its **own state** + `clipFinished` transition back to Idle/Run | Leave locomotion, play the attack, return when the clip ends                  | Preferred in many production graphs              |

If locomotion “doesn’t play,” check whether the Idle or Run clip is marked one-shot / `LoopOnce`, or whether the clip sits only in `oneShotClips` instead of on a looping state. Do not put the same motion in both a one-shot list and a state without testing.

## When animation does not play

Open the character’s `.animconfig.json` in the Model Viewer (or review the graph the agent edited). Work this list **before** asking the AI to rewrite pawn code.

| Check                           | What to look for                                                                                                                                                                                                                    |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1. `configUrl`**              | Select the **Animation State Machine Node**. `configUrl` points at the `.animconfig.json` you mean (`@project/...` or `@engine/...`).                                                                                               |
| **2. Clip exists on the model** | Model Viewer lists the clip on the `.glb`. If the list is empty, the file is not a usable animated rig. [How to import a rigged character and animation clips](/the-editor/how-to-import-a-rigged-character-and-animation-clips.md) |
| **3. Clip is in this config**   | The clip is assigned on a state (or listed via **Sources** / `externalAnimationSources`). A clip only on disk does nothing.                                                                                                         |
| **4. Transition lines**         | The states you care about are connected. Idle ↔ Run needs lines **and** conditions. Missing lines = parameters never change the pose.                                                                                               |
| **5. One-shot / loop**          | Locomotion clips loop. Attacks may be one-shot or `LoopOnce`. A looping idle stuck as one-shot will not behave like Idle.                                                                                                           |
| **6. Parameters**               | Names in code match the graph exactly (`isRunning`, not `running`). Default humanoids also need directional weights; `isRunning: true` with `forward/back/left/right` all `0` can look idle inside Run.                             |
| **7. Who writes parameters**    | Character Pawn templates write locomotion for you. A custom pawn must call `setParameter` (or `transitionGraphToState` for forced states).                                                                                          |

Most “AI can’t find it” cases are rows **3**, **4**, or **5**.

## How Sandbox Studio wires it (v14)

| Piece                  | What it is                                        | Where it lives                     |
| ---------------------- | ------------------------------------------------- | ---------------------------------- |
| **Animation Config**   | The graph: clips, states, transitions, parameters | `assets/.../*.animconfig.json`     |
| **State machine node** | Plays that graph on a skeleton                    | `ENGINE.AnimationStateMachineNode` |
| **Runtime writes**     | Changes parameters (or forces a graph state)      | Your pawn / node TypeScript        |

Create the node in code the same way default characters do:

```typescript
const animation = ENGINE.AnimationStateMachineNode.create({
  name: 'AnimationComponent',
  configUrl: '@project/assets/character/your-character.animconfig.json',
});
this.add(animation);
```

`configUrl` is always a fully qualified `@project/assets/...` or `@engine/assets/...` path. After you add a class, run `pnpm build` and **Build Project** (`Ctrl+B` / `Cmd+B`). See [Project code fundamentals](/best-practices/project-code-fundamentals.md).

Find the node at runtime:

```typescript
const anim = this.getNode(ENGINE.AnimationStateMachineNode);
if (!anim?.isReady()) {
  return;
}
```

### Parameters the default humanoid graph expects

Movement templates and `CharacterPawn` already create an `AnimationStateMachineNode` and push locomotion into the graph. You do not rebuild Idle/Run for those templates unless you replace the config.

A shipping character config uses this parameter set (names must match the graph exactly):

| Parameter   | Type    | Role                            |
| ----------- | ------- | ------------------------------- |
| `isRunning` | Boolean | Idle ↔ run                      |
| `isJumping` | Boolean | Jump                            |
| `forward`   | Number  | Weight of the run-forward clip  |
| `back`      | Number  | Weight of the run-backward clip |
| `left`      | Number  | Weight of strafe-left           |
| `right`     | Number  | Weight of strafe-right          |

Write several at once:

```typescript
anim.setParameter({
  isRunning: true,
  isJumping: false,
  forward: 1,
  back: 0,
  left: 0,
  right: 0,
});
```

A **Run** state is often a **blend**, not a single clip: each directional clip is weighted by `forward` / `back` / `left` / `right`. If you only set `isRunning` and leave weights at 0, the character can enter Run and still look idle.

### Attacks and other one-off motion

The Model Viewer can mark **one-shot clips** (play once over the current state). Many production graphs instead add **extra states** (for example an attack state) and leave `oneShotClips` empty. Those states return with `{ "clipFinished": true }`.

Combat code often calls `anim.transitionGraphToState('base', 'attackState')` (or back to `'idle'`) rather than hoping a one-shot mixer interrupt. If the agent is adding melee animation, match that pattern: named states + `clipFinished` + `transitionGraphToState`, unless the config you opened actually lists one-shot clips.

Event markers on clips (for example `footstep`, `hitStart` / `hitEnd` at a time in seconds) fire during playback so audio and hitboxes can sync. Layers, bone masks, and skeleton retargeting exist in the same system. Import, profiles, extra clip `.glb` files, and attaching a held mesh: [How to import a rigged character and animation clips](/the-editor/how-to-import-a-rigged-character-and-animation-clips.md).

## What to ask the agent

MCP does not have a special animation tool. The agent edits the config and the pawn the same way it edits other assets and TypeScript. Be specific. **Tell it to inspect the graph before rewriting code:**

* “Open `@project/assets/character/<name>.animconfig.json`. Confirm the clip is in the config, Idle/Run have transition lines with conditions, and locomotion clips loop (not one-shot). Then fix only what is missing.”
* “Add Idle and a blended Run using `isRunning`, `forward`, `back`, `left`, `right`. Point the pawn’s `AnimationStateMachineNode.configUrl` at that file.”
* “Do not play clips by calling Three.js actions from gameplay. Use `setParameter` or `transitionGraphToState`.” What Three.js is in Studio: [What Three.js means in Sandbox Studio](/the-editor/what-threejs-means.md).
* “Do not open `.genesys-scene` files to bind animation. Set `configUrl` on the node.”

Use [Sandbox Studio MCP](/working-efficiently-with-ai/set-up-and-use-sandbox-studio-mcp.md) so the agent can place nodes and build the project after the change.

## Build the graph in the editor

{% stepper %}
{% step %}

## Add the animated model

Place the model in the scene and adjust its scale if needed. Confirm that the asset is rigged and includes animation clips before continuing.

{% hint style="info" %}
*NOTE: A model needs a compatible skeleton and animation data before its clips can be used. A static or unrigged model will not provide the animation options shown in this workflow.*
{% endhint %}
{% endstep %}

{% step %}

## Inspect the model

In the Models folder, right-click the asset and select Open in Model Viewer. The viewer shows the model's attached data and lists any available animation clips.
{% endstep %}

{% step %}

## Create an Animation Config

Right-click the model and choose the option to create an Animation Config from the asset. Enter a clear name, then create and open the new config.

{% hint style="info" %}
*TIP: You can also right-click an empty area and create a new Animation Config resource. Creating it from the model is useful when you want the model and its existing clips connected from the start.*
{% endhint %}
{% endstep %}

{% step %}

## Preview the clips

Use the preview area to select each available clip and play it on the mesh. Check that the motion, timing, and skeleton behave as expected before building the state machine.
{% endstep %}

{% step %}

## Create the initial state

Right-click an empty area of the graph and create a state. Give it a name such as Idle, choose Single Clip as its type, assign the idle animation, and enable looping when the clip should repeat.
{% endstep %}

{% step %}

## Set the initial state

Mark the Idle state as the initial state. The state machine starts here whenever the animation system begins. In the JSON this is `initialState`.
{% endstep %}

{% step %}

## Configure one-shot clips

Open the config settings and enable the clips that should play once over the current state. Use one-shot clips for temporary actions such as an attack, reaction, or other motion that should not replace the normal state permanently.

If you are following a template character, check whether attacks are already **states** with `clipFinished` instead. Do not add both a one-shot and a state for the same clip without testing.

Leave locomotion clips **out** of the one-shot list. Idle and Run should loop on their states.

The agent can generate and adjust state machines when you ask. Review the graph and test it in Play mode.
{% endstep %}

{% step %}

## Add another state

Right-click the graph, create another state, and assign its clip. For example, create a Run state. For directional movement, prefer a **blend** state: Run, Run Backward, StrafeLeft, StrafeRight weighted by `forward`, `back`, `left`, `right`. Enable looping on locomotion clips.
{% endstep %}

{% step %}

## Connect the states

Create a transition from Idle to Run. Select the transition **line** to set its source, destination, blend duration, and condition. Without this line, `isRunning` will not move the character into Run.

{% hint style="info" %}
*TIP: Blend duration controls how quickly one state mixes into the next. Increase it for a smoother change or reduce it when the transition needs to feel immediate.*
{% endhint %}
{% endstep %}

{% step %}

## Create a transition parameter

Add a clearly named parameter for the transition. Use a Boolean when the condition is true or false (`isRunning`), or a Number when the transition or a blend weight depends on a threshold (`forward`).
{% endstep %}

{% step %}

## Set the transition condition

Choose the parameter on the Idle-to-Run transition, then select its comparison. A Boolean can use equal or not equal, while a Number can use comparisons such as greater than, equal to, or less than a chosen value.
{% endstep %}

{% step %}

## Create the return transition

Connect Run back to Idle and set the opposite condition. For example, enter Run when `isRunning` is true (and directional weights are non-zero), and return to Idle when `isRunning` is false. Both directions need lines.
{% endstep %}

{% step %}

## Add animation sources

Open Sources in the menu when clips need to come from another animation asset. Select the character model or skeleton and the animation file, then add the compatible clips to the config. Paths in the file are `@engine/assets/...` or `@project/assets/...`.

{% hint style="info" %}
*NOTE: Check skeleton compatibility when adding another source, including assets based on a mannequin or Mixamo skeleton. An incompatible source may require retargeting before its clips work correctly on the model.*
{% endhint %}
{% endstep %}

{% step %}

## Assign the config to the character

1. Select the character or the `Animation State Machine Node` in the Outliner.
2. In the Inspector, set `configUrl` to the `.animconfig.json` you created.

Use this when you built a custom config or placed a model yourself. Movement templates already point at a humanoid config.
{% endstep %}

{% step %}

## Drive a parameter at runtime

From your pawn or node, after the machine is ready:

```typescript
const anim = this.getNode(ENGINE.AnimationStateMachineNode);
anim.setParameter({ isRunning: true, forward: 1 });
```

Enter Play mode. The character should leave Idle, blend into Run, and return when you set `isRunning: false` and the directional weights to `0`.
{% endstep %}

{% step %}

## Test and refine the state machine

Trigger each state, one-shot or `clipFinished` path, and return transition. Adjust blend durations, conditions, parameter values, or sources when a change feels late, abrupt, or incorrect. If nothing moves, return to **When animation does not play** above.

{% hint style="info" %}
*TIP: Ask the Sandbox Studio agent to create or adjust states and transitions, then review the `.animconfig.json` and test every path in Play mode.*
{% endhint %}
{% endstep %}
{% endstepper %}

## What You've Done

You know the animation **config** is the `.animconfig.json` graph (states, transition lines, parameters, clip loop settings), the **state machine node** plays it through `configUrl`, and motion only changes when parameters (or `transitionGraphToState`) change. You can check missing clips, missing lines, and one-shot mistakes before handing the problem to the AI. Imported rigs and extra clip files: [How to import a rigged character and animation clips](/the-editor/how-to-import-a-rigged-character-and-animation-clips.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://studio-docs.sandbox.game/the-editor/create-an-animation-state-machine.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
