Skip to content

Animation scripting

Drive an entity's animator from C#: set parameters, switch states directly, and react when a state changes.

Getting the Animator

csharp
using JoystickEngine;

public class Player : GameEntity
{
    private Animator? m_Animator;

    protected override void OnCreate()
    {
        m_Animator = GetComponent<Animator>();
    }
}

The entity needs an Animator component with a controller assigned. GetComponent<Animator>() returns null if it doesn't have one.

Placeholder: the Animator component in the Inspector, showing Controller, Play On Start, and Speed

Setting parameters

Parameters are the inputs your controller's transitions test against — see Animator controllers for how they're declared.

csharp
m_Animator?.SetFloat("Speed", velocity.Length());
m_Animator?.SetBool("Grounded", isOnGround);
m_Animator?.SetInt("ComboStep", combo);
m_Animator?.SetTrigger("Attack");
m_Animator?.ResetTrigger("Attack");
MethodUse for
SetFloat(name, value)Continuous values — speed, health fraction
SetInt(name, value)Counters and discrete modes
SetBool(name, value)On/off conditions
SetTrigger(name)One-shot events; consumed automatically once a transition takes it
ResetTrigger(name)Cancels a pending trigger before it's consumed

Matching getters exist: GetFloat(name), GetInt(name), GetBool(name).

Reading state

csharp
if (m_Animator?.CurrentState == "Attack" && m_Animator.NormalizedTime > 0.9f)
    canQueueNextAttack = true;
MemberTypeMeaning
CurrentStatestringName of the state playing now
NormalizedTimefloatProgress through the current clip, 0 to 1
IsTransitioningboolWhether a transition is in progress
IsPlayingboolWhether the animator is running (not paused)
SpeedfloatPlayback multiplier; read and write

Controlling playback directly

csharp
m_Animator?.Play("Idle");            // switch immediately, restart the clip
m_Animator?.CrossFade("Run", 0.1f);  // blend into this state over 0.1s
m_Animator?.Pause();
m_Animator?.Resume();
MethodMeaning
Play(state, normalizedTime = 0)Switch to a state immediately, optionally starting partway through
CrossFade(state, duration)Blend into a state over duration seconds
Pause() / Resume()Freeze and unfreeze the animator

These are the same transition mechanics a .janimator file's own transitions use — property values blend, sprite frames snap halfway through. Prefer parameters and transitions authored on the controller for anything condition-driven; reach for Play/CrossFade when your script needs to force a specific state directly.

Reacting to state changes

Override these on your GameEntity subclass — not events on the Animator component itself, since a fresh Animator object is handed back every time you call GetComponent<Animator>(), and only overrides on your own persistent script instance can reliably hear about state changes:

csharp
public class Player : GameEntity
{
    protected override void OnAnimatorStateEntered(string stateName)
    {
        if (stateName == "Attack")
            m_HitboxOpen = true;
    }

    protected override void OnAnimatorStateExited(string stateName)
    {
        if (stateName == "Attack")
            m_HitboxOpen = false;
    }
}

Both fire once per transition, Exited immediately before Entered for the same switch — including transitions taken from "Any". These fire for any Animator component on the entity; if you have more than one, check stateName against the states you expect from each.

Layering motion on top of a pose

csharp
public class Player : GameEntity
{
    protected override void OnAnimatorPose()
    {
        var head = FindChild("Head")!;
        head.Translation += new Vector3(0.0f, m_RecoilOffset, 0.0f);
    }
}

OnAnimatorPose fires every frame, right after the animator writes that frame's sampled pose — the hook for procedural motion (recoil, look-at, breathing) that should sit on top of a clip without next frame's sample overwriting it.

Animation events

Mark a method [AnimationEvent] to make it callable by name from a clip's event track:

csharp
public class Player : GameEntity
{
    private AudioSourceComponent? m_Footstep;
    private bool m_HitboxOpen;

    protected override void OnCreate()
    {
        m_Footstep = GetComponent<AudioSourceComponent>();
    }

    [AnimationEvent]
    void OnFootstep() => m_Footstep?.Play();

    [AnimationEvent]
    void OpenHitbox() => m_HitboxOpen = true;

    [AnimationEvent]
    void CloseHitbox() => m_HitboxOpen = false;
}

Supported signatures: no parameters, or a single string, float, or int. The event key in the .janim file supplies whichever value matches — see Asset files for how event keys are authored. A misspelled or missing method name is reported once in the Log panel and the event is skipped; nothing throws.

Animation.Sample

csharp
Animation.Sample(entity, "Animations/Idle.janim", 0.25f);

Intended for posing an entity to a specific point of a clip with no Animator component involved — thumbnails, cutscene setup, editor tooling.

Not implemented yet

This call is a placeholder in the current build: it logs that it was called but doesn't actually pose the entity. Use an Animator component and Play(state, normalizedTime) for anything that needs to work today.

See also

Two other API Reference topics come up often alongside animation scripting:

  • Physics2D — a ground check (Physics2D.Raycast) is the usual way to drive the Grounded parameter behind a jump/land transition.
  • Session — carries a value (which outfit is equipped, a combo counter) across a scene change, so an Animator picking a state based on that value stays correct after Scenes.Load.