> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/core-engine-concepts/spine.md).

# Spine Animations

Spine is how animated characters and props work in All Out (chests, animals, props, VFX rigs, and the player).

To make Spine feel simpler, keep this mental model:

* **Spine rig (`.spine`)**: the “blueprint” (bones + animation names + skin names)
* **`Spine_Animator`**: a component on an entity that renders/updates a Spine rig in the world
* **`Spine_Instance`**: a standalone (non-component) instance — used for UI animations or manual lifetime control
* **Skins**: “outfits/variants” that decide which images are visible
* **Animations**: named timelines (e.g. `"idle"`, `"walk"`, `"open"`)

### When to use `Spine_Animator` vs `Spine_Instance`

* Use **`Spine_Animator`** for anything that exists in the world (props, NPCs, interactables).
* Use **`Spine_Instance`** directly when you want to draw a Spine in UI or you need manual lifetime control.

{% hint style="warning" %}
If you call `Spine_Instance.create()`, you must call `instance.destroy()` when you're done (otherwise you leak).
{% endhint %}

### Spine API reference (CSL)

```go
// Component — attached to world entities
Spine_Animator :: class : Component {
    depth_offset: float;
    layer: s32;
    instance: Spine_Instance #read_only;

    // Visuals
    color_multiplier: v4;
    scale: v2;
    speed_multiplier: float;
    state_machine: State_Machine #read_only;
    color_replace_color: Color_Replace_Color #read_only;

    awaken :: method();

    set_skeleton :: method(asset: Spine_Asset);
    get_skeleton :: method() -> Spine_Asset;

    set_animation :: method(animation: string, loop: bool, track: s64, speed: float = 1);

    // Skins
    set_skin          :: method(skin: string);
    enable_skin       :: method(skin: string);
    disable_skin      :: method(skin: string);
    disable_all_skins :: method();
    refresh_skins     :: method();
    set_to_setup_pose :: method();
    get_skins         :: method() -> []string;

    // Bone local offsets (advanced)
    get_bone_local_position :: method(bone_name: string) -> v2;
    set_bone_local_position :: method(bone_name: string, position: v2);

    // Optional: drive animations via a state machine
    set_state_machine       :: method(new_state_machine: State_Machine, transfer_ownership: bool);
    set_color_replace_color :: method(color: Color_Replace_Color);
    set_material            :: method(material: Material, transfer_ownership: bool);
}

Spine_Event_Data :: struct {
    event: string;
    int_value: s64;
    float_value: float;
    string_value: string;
}

// Standalone instance — for UI or manual lifetime control
Spine_Instance :: class {
    create  :: proc() -> Spine_Instance;
    destroy :: method();
    update  :: method(dt: float);

    set_on_event           :: method(userdata: Object, callback: proc(userdata: Object, event: Spine_Event_Data));
    set_on_animation_start :: method(userdata: Object, callback: proc(userdata: Object, animation: string));
    set_on_animation_end   :: method(userdata: Object, callback: proc(userdata: Object, animation: string));

    // (same methods as Spine_Animator above, plus all the same fields)
}
```

### Adding animated objects to your world

There are 3 ways to add animated objects to your scene, all of which will create a Spine\_Animator component.

* Drag any animated asset from the [Asset Catalog](/using-the-editor/asset-catalog.md)into your scene
* Create a new entity and add the Spine\_Animator component
  * Set the `Skeleton Data Asset` field to a .spine file in your assets
* Add a spine animator to your world using scripting (covered in the examples)

{% hint style="info" %}
If your code and the `Spine_Animator` are on the same entity and start at the same time, call `spine.awaken()` before calling any animation methods.
{% endhint %}

### Examples

#### Openable Chest

An interactable chest that plays an “open” animation and then stays open:

```go
Chest :: class : Interactable {
    opened: bool;

    ao_start :: method() {
        this.set_listener(this);
        this.set_text("Open");
        radius = 1.25;
        required_hold_time = 0.15;

        spine := entity.get_component(Spine_Animator);
        spine.awaken();
        spine.set_animation("idle", true, 0);
    }

    can_use :: method(player: Player) -> bool {
        return !opened;
    }

    on_interact :: method(player: Player) {
        opened = true;
        this.set_text("Opened");

        spine := entity.get_component(Spine_Animator);
        spine.set_animation("open", false, 0);
    }
}
```

#### Walking Chicken

A simple “idle vs walk” loop based on movement:

```go
Chicken :: class : Component {
    speed: float @ao_serialize;
    last_pos: v2;
    was_moving: bool;

    ao_start :: method() {
        speed = 1.5;
        last_pos = entity.world_position;

        spine := entity.get_component(Spine_Animator);
        spine.awaken();
        spine.set_animation("idle", true, 0);
    }

    ao_update :: method(dt: float) {
        // Example motion (patrol)
        entity.add_local_position({speed * dt, 0});

        moving := length_squared(entity.world_position - last_pos) > 0.0001;
        last_pos = entity.world_position;

        if moving != was_moving {
            was_moving = moving;
            spine := entity.get_component(Spine_Animator);
            spine.set_animation(moving ? "walk" : "idle", true, 0);
        }
    }
}
```

#### Driveable Car

If you want "driveable" behavior, the simplest approach is to drive the *movement* with your own gameplay logic, and drive the *animation* based on whether the car is moving.

```go
Car :: class : Component {
    agent: Movement_Agent @ao_serialize;
    target: Entity @ao_serialize;

    ao_start :: method() {
        spine := entity.get_component(Spine_Animator);
        spine.awaken();
        spine.set_animation("idle", true, 0);
    }

    ao_update :: method(dt: float) {
        if #alive(target) {
            agent.set_path_target(target.world_position, agent.movement_speed);
        }

        moving := length_squared(agent.velocity) > 0.01;
        spine := entity.get_component(Spine_Animator);
        spine.set_animation(moving ? "drive" : "idle", true, 0);
    }
}
```

{% hint style="info" %}
If you're switching between only two looping animations, direct `set_animation` calls are usually simpler than building a state machine.
{% endhint %}

### Skins (variants/outfits)

Some spines don't start with a default skin. If your entity is "invisible", you likely need to pick a skin.

```go
spine := entity.get_component(Spine_Animator);
spine.awaken();

// Option A: set a single skin
spine.set_skin("default");
spine.refresh_skins();

// Option B: combine multiple skins
spine.disable_all_skins();
spine.enable_skin("body");
spine.enable_skin("hat");
spine.refresh_skins();
```

{% hint style="warning" %}
Always call `refresh_skins()` after changing skins.
{% endhint %}

### Animation events

Use callbacks when a Spine timeline event should trigger gameplay or audio. For a `Spine_Animator` component, register callbacks on its `instance`.

```go
Spine_Event_Listener :: class : Component {
    ao_start :: method() {
        spine := entity.get_component(Spine_Animator);
        spine.awaken();

        spine.instance.set_on_event(this, proc(userdata: Object, event: Spine_Event_Data) {
            listener := userdata.(Spine_Event_Listener);
            listener.handle_spine_event(event);
        });
    }

    handle_spine_event :: method(event: Spine_Event_Data) {
        if event.event == "footstep" {
            desc := SFX.default_sfx_desc();
            desc.set_position(entity.world_position);
            SFX.play(get_asset(SFX_Asset, "sfx/footstep.wav"), desc);
        }
    }
}
```

### State machines (optional, for complex animation logic)

You can keep animation logic simple by calling `set_animation` directly. If you have many states (idle/run/attack/hit/death), a `State_Machine` helps you define transitions once and then just set variables/triggers.

The key idea: **state names must match animation names** in your Spine file.

#### What a state machine does

Think of a state machine as a tiny “animation controller”:

* You define **states** (each state name should match a Spine animation name)
* You define **variables** (`bool`, `trigger`, `int`, `float`)
* You define **transitions** between states based on those variables
* At runtime, you only update variables (the state machine chooses the animation)

#### Minimal example: idle/walk + attack trigger

```go
NPC_Anim :: class : Component {
    state_machine: State_Machine;
    is_moving: bool;

    ao_start :: method() {
        // 1) Create the state machine + variables
        state_machine = State_Machine.create();
        moving_var := state_machine.create_variable("is_moving", .BOOL);
        attack_var := state_machine.create_variable("attack", .TRIGGER);

        // 2) Create a layer (maps to a Spine track, usually 0)
        layer := state_machine.create_layer("main", 0);

        // 3) Create states (names must match animations in the Spine rig)
        idle := layer.create_state("idle", true);
        walk := layer.create_state("walk", true);
        attack := layer.create_state("attack", false);
        layer.set_initial_state(idle);

        // 4) Movement transitions
        idle_to_walk := layer.create_transition(idle, walk, false);
        idle_to_walk.create_bool_condition(moving_var, true);

        walk_to_idle := layer.create_transition(walk, idle, false);
        walk_to_idle.create_bool_condition(moving_var, false);

        // 5) Attack from any state, then return to idle when complete
        to_attack := layer.create_global_transition(attack, true);
        to_attack.create_trigger_condition(attack_var);

        attack_to_idle := layer.create_transition(attack, idle, true); // require_state_complete = true

        // 6) Attach to the animator
        spine := entity.get_component(Spine_Animator);
        spine.awaken();
        spine.set_state_machine(state_machine, true); // transfer_ownership = true
    }

    ao_update :: method(dt: float) {
        state_machine.set_bool("is_moving", is_moving);
    }

    do_attack :: method() {
        state_machine.set_trigger("attack");
    }
}
```

{% hint style="info" %}
If you pass `transfer_ownership = true` to `set_state_machine`, the animator will destroy the state machine for you. Otherwise, you must destroy it yourself.
{% endhint %}

### Troubleshooting

Q: I dragged a spine asset into my scene and I don't see any object, it's just an empty entity

A: Make sure to click "Add Skin" and select a variant of the spine to apply! Some spines don't start with a default skin.

Q: My script crashes when I call animation methods on my Spine\_Animator

A: If your script and the `Spine_Animator` start at the same time, call `spine.awaken()` before calling any animation methods.

Q: I changed skins but nothing happened

A: After any skin change (`set_skin`, `enable_skin`, `disable_skin`, `disable_all_skins`), you must call `refresh_skins()`.

### Custom Player Skins/Animations

If you want to add custom animations to your player, you can do so by merging the base All Out player spine rig together with a custom rig containing more animations (we can provide many of these, just contact us[Developer Support](/going-all-out/developer-support.md))

### The Spine Rig Format

Spines rigs are made up of 3 files (automatically downloaded for you when you use the Asset Catalog)

* The skeleton file (.spine) provides information about the bones, skins, and structure of your animated object.
* The atlas image (.png) has the actual textures used to draw the spine in the world.
* The atlas file (.atlas) provides information about where inside the .png atlas image each part of the animated object is.

###

### Creating your own animations

{% hint style="warning" %}
Currently to make your own animations or rigs, you'll need to own a copy of Esoteric Spine. However, we provide a massive library of existing spine rigs and animations as part of the [Asset Catalog](/using-the-editor/asset-catalog.md)for you to use and we recommend starting there!
{% endhint %}

#### Export Format

To use a custom Spine rig in All Out, you need to export the standard 3-file Spine bundle into your `res` folder:

* `something.spine`
* `something.atlas`
* `something.png` (atlas texture)

If you're unsure about export settings, start from an existing All Out Spine asset from the Asset Catalog and mirror its structure or reach out to us!


---

# 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://docs.allout.game/core-engine-concepts/spine.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.
