# Animator

> Talos 0.6 · Since 0.6

Present on `ctx.animator` when the entity has an animator component.

Reached in a behaviour as `ctx.animator`.

## Signature

```ts
export interface Animator {
  /** Switch to a state defined in the component; unknown states are a contract error. */
  set(state: string): void
  readonly state: string | null
  /**
   * Writes a blend-tree parameter — `ctx.animator.param("speed", 3.2)`.
   *
   * Every parameter a blend tree reads is declared in the component's `parameters`, so the
   * names that work are written down in the scene. A parameter the component does not
   * declare is refused: it throws an `Error` whose `name` is `AnimatorParamError` and whose
   * message opens with `animator-param-unknown`. A typo would otherwise leave the blend
   * frozen with nothing anywhere saying why.
   *
   * A parameter with a `source` is written by the engine every tick from the character
   * controller, so writing it here is overwritten on the next tick. Leave `source` off the
   * ones a behavior drives.
   *
   * A state carrying `rootMotion` must not read a parameter bound to `speed`, `strafeX` or
   * `strafeY`: root motion writes the velocity those report, so the tree would be reading
   * its own output and the character would never leave its first clip. Bind it to
   * `inputSpeed`, `inputStrafeX` or `inputStrafeY` — what the controller was asked for — or
   * write it here. The contract reports the combination as `animator-root-motion-source`.
   */
  param(name: string, value: number): void
  /**
   * How far root motion moved this entity, in world space and in metres.
   *
   * "On the last tick" inside `update`, where the animator has not run yet; this tick's
   * inside an `onEvent` handler, because animation events are delivered after the animator
   * pass that raised them. One tick apart, never wrong, only differently old.
   *
   * Zero unless the playing state sets `rootMotion: true`. A character with a `controller`
   * component has already been moved by it — the engine hands the delta to the controller,
   * which resolves it against the world — so reading it there is for inspection. An entity
   * with no controller has been moved by it too, on its transform. A behavior that wants to
   * own the movement takes this and applies it itself.
   */
  rootDelta(): Vec3
  /**
   * Points an IK chain's end bone at a world-space point, or releases it with `null`.
   *
   * The chain is declared in the component's `ik`, so the names that work are written down
   * in the scene; one it does not declare throws an `Error` whose `name` is
   * `AnimatorIkError`. A released chain returns to the pose the clips produced.
   *
   * The engine owns the solve and the game owns the target: where a foot should go is a game
   * question, because a stair tread and a puddle are the same collision geometry. Call it
   * every tick a foot is planted — the target is remembered, so a character standing still
   * costs one call and no bookkeeping.
   *
   * `weight` is the chain's blend, 0..1, applied whether or not the target moved. **Nothing
   * ramps it for you**: 0 to 1 in one tick snaps the foot, and the engine cannot tell a
   * footfall from a stance change, so the caller ramps it over a handful of ticks.
   *
   * The target may be an entity handle as well as a point, in which case its position is
   * used.
   */
  ik(chain: string, target: Vec3 | EntityHandle | null, weight?: number): void
  /**
   * Aims the look-at chain at a world-space point, or releases it with `null`.
   *
   * Throws `AnimatorLookAtError` when the component declares no `lookAt`. Releasing eases the
   * head back to whatever the clip is doing over the constraint's own `smoothing`, rather
   * than dropping the aim in one frame.
   */
  lookAt(target: Vec3 | EntityHandle | null): void
  /**
   * Plays a state, or a clip of the animator's asset that no state plays, **once** — and then
   * hands over by itself: to `opts.then`, or back to the state that was playing before.
   *
   * `ctx.animator.play("reload", { interrupt: ["die"] })` is a reload that nothing but death
   * cuts short; `ctx.animator.play("Wave")` is an emote straight from a clip name, with no
   * state authored for it; a door's `play("open", { then: "opened" })` swings once and rests.
   * A state already declared with `once` in the scene keeps its declared behaviour and the
   * options override it field by field.
   *
   * The hand-over starts `blendOut` seconds before the clip's last frame, on the tick the clip
   * gets there — deterministic, a function of the clip and the tick rate only. When it ends —
   * run out or cut short — the bus event `opts.event` (default `"animation-end"`) is raised
   * with {@link AnimationEndEvent} as its data. Calling `play` again with the one-shot that is
   * playing starts it over, which a `set` of the playing state never does.
   *
   * Returns `false`, changing nothing, when the one-shot playing now does not let this one cut
   * in (its `interrupt` list leaves it out). Throws an `Error` whose `name` is
   * `AnimatorPlayError` for a name that is neither a state nor a clip, a `then` that is not a
   * state (message opening `animator-play-unknown`), or a negative blend time
   * (`animator-play-invalid`).
   */
  play(name: string, opts?: AnimatorPlayOptions): boolean
  /**
   * The animator-wide playback multiplier: 1 (the default) plays every state at its own rate,
   * 2 doubles it, 0 freezes the pose where it is. It multiplies each state's `speed` and
   * `speedParam` from the scene, so it is the knob for one character's slow motion or a
   * hit-stop, not for pacing a state — declare `speed` on the state for that. Throws
   * `RangeError` below 0 or when not finite. The effective rate, every multiplier in, is
   * `anim.speed` on `GET /entities`.
   */
  speed: number
}
```

Import: `import type { Animator } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `set` | `set(state: string): void` | Switch to a state defined in the component; unknown states are a contract error. |
| `state` | `readonly state: string \| null` |  |
| `param` | `param(name: string, value: number): void` | Writes a blend-tree parameter — `ctx.animator.param("speed", 3.2)`. Every parameter a blend tree reads is declared in the component's `parameters`, so the names that work are written down in the scene. A parameter the component does not declare is refused: it throws an `Error` whose `name` is `AnimatorParamError` and whose message opens with `animator-param-unknown`. A typo would otherwise leave the blend frozen with nothing anywhere saying why. A parameter with a `source` is written by the engine every tick from the character controller, so writing it here is overwritten on the next tick. Leave `source` off the ones a behavior drives. A state carrying `rootMotion` must not read a parameter bound to `speed`, `strafeX` or `strafeY`: root motion writes the velocity those report, so the tree would be reading its own output and the character would never leave its first clip. Bind it to `inputSpeed`, `inputStrafeX` or `inputStrafeY` — what the controller was asked for — or write it here. The contract reports the combination as `animator-root-motion-source`. |
| `rootDelta` | `rootDelta(): Vec3` | How far root motion moved this entity, in world space and in metres. "On the last tick" inside `update`, where the animator has not run yet; this tick's inside an `onEvent` handler, because animation events are delivered after the animator pass that raised them. One tick apart, never wrong, only differently old. Zero unless the playing state sets `rootMotion: true`. A character with a `controller` component has already been moved by it — the engine hands the delta to the controller, which resolves it against the world — so reading it there is for inspection. An entity with no controller has been moved by it too, on its transform. A behavior that wants to own the movement takes this and applies it itself. |
| `ik` | `ik(chain: string, target: Vec3 \| EntityHandle \| null, weight?: number): void` | Points an IK chain's end bone at a world-space point, or releases it with `null`. The chain is declared in the component's `ik`, so the names that work are written down in the scene; one it does not declare throws an `Error` whose `name` is `AnimatorIkError`. A released chain returns to the pose the clips produced. The engine owns the solve and the game owns the target: where a foot should go is a game question, because a stair tread and a puddle are the same collision geometry. Call it every tick a foot is planted — the target is remembered, so a character standing still costs one call and no bookkeeping. `weight` is the chain's blend, 0..1, applied whether or not the target moved. **Nothing ramps it for you**: 0 to 1 in one tick snaps the foot, and the engine cannot tell a footfall from a stance change, so the caller ramps it over a handful of ticks. The target may be an entity handle as well as a point, in which case its position is used. |
| `lookAt` | `lookAt(target: Vec3 \| EntityHandle \| null): void` | Aims the look-at chain at a world-space point, or releases it with `null`. Throws `AnimatorLookAtError` when the component declares no `lookAt`. Releasing eases the head back to whatever the clip is doing over the constraint's own `smoothing`, rather than dropping the aim in one frame. |
| `play` | `play(name: string, opts?: AnimatorPlayOptions): boolean` | Plays a state, or a clip of the animator's asset that no state plays, **once** — and then hands over by itself: to `opts.then`, or back to the state that was playing before. `ctx.animator.play("reload", { interrupt: ["die"] })` is a reload that nothing but death cuts short; `ctx.animator.play("Wave")` is an emote straight from a clip name, with no state authored for it; a door's `play("open", { then: "opened" })` swings once and rests. A state already declared with `once` in the scene keeps its declared behaviour and the options override it field by field. The hand-over starts `blendOut` seconds before the clip's last frame, on the tick the clip gets there — deterministic, a function of the clip and the tick rate only. When it ends — run out or cut short — the bus event `opts.event` (default `"animation-end"`) is raised with {@link AnimationEndEvent} as its data. Calling `play` again with the one-shot that is playing starts it over, which a `set` of the playing state never does. Returns `false`, changing nothing, when the one-shot playing now does not let this one cut in (its `interrupt` list leaves it out). Throws an `Error` whose `name` is `AnimatorPlayError` for a name that is neither a state nor a clip, a `then` that is not a state (message opening `animator-play-unknown`), or a negative blend time (`animator-play-invalid`). |
| `speed` | `speed: number` | The animator-wide playback multiplier: 1 (the default) plays every state at its own rate, 2 doubles it, 0 freezes the pose where it is. It multiplies each state's `speed` and `speedParam` from the scene, so it is the knob for one character's slow motion or a hit-stop, not for pacing a state — declare `speed` on the state for that. Throws `RangeError` below 0 or when not finite. The effective rate, every multiplier in, is `anim.speed` on `GET /entities`. |

## Example

The tested fixture `fixtures/app/agent.ts`.

```ts
import type { Behavior } from "@argus/api"

// Sends a nav agent to `props.target` once, then reports arrival through ctx.state so the
// test can read it without polling the crowd directly.
export default {
  init(ctx) {
    ctx.state.arrived = false
    ctx.nav?.goTo(ctx.props.target as [number, number, number])
  },
  update(ctx) {
    if (ctx.nav?.arrived) ctx.state.arrived = true
  },
} satisfies Behavior
```
