Spine Events & AnimationState callbacks

Updated 2017 Feb 6

Spine.AnimationState provides functionality for animation callbacks in the form of C# events. You can use these to handle some basic points of animation playback.

For the novice programmer: Callbacks mean you can tell the system to inform you when something specific happens by giving it a method to call when that something happens. Events are the meaningful points in program execution— in this case, that you can subscribe to/handle with your own code by providing a function/method for the event system to call.

When the event happens, the process of calling the function/method you provided is called "raising" or "firing" the event. Most C# documentation will call it "raising". But Spine documentation will call it "firing". Those mean the same thing. The structure and syntax for callback functionality varies from language to language. See the sample code at the bottom for examples of C# syntax.

Fig 1. Chart of Events raised without mixing/crossfading.

Spine.AnimationState raises the following events:

  • Start is raised when an animation starts playing,
    • This applies to right when you call SetAnimation.
    • I can also be raised when a queued animation starts playing.
  • End is raised when an animation is cleared (or interrupted),
    • This applies to immediately after the animation is done mixing/transitioning out.
    • After end is fired, that entry will never be applied again.
    • This is also raised when you clear the track using ClearTrack or ClearTracks.
    • NEVER handle the End event with a method that calls SetAnimation. See the warning below.
  • Interrupt is raised when another TrackEntry has interuppted the current TrackEntry.
    • This entry may continue to be applied for mixing.
  • Dispose is raised when the TrackEntry is disposed.
    • This may occur without the entry ever being set as the current entry.
    • References to the entry should not be kept after dispose is called, as it may be destroyed or reused.
  • Complete is raised an animation completes its full duration,
    • This is raised when a non-looping animation finishes playing, whether or not a next animation is queued.
    • This is also raised every time a looping animation finishes an loop.
  • Event is raised whenever ANY user-defined event is detected.
    • These are events you keyed in animations in Spine editor. They are purple keys. A purple icon can also be found in the Tree view.
    • To distinguish between different events, you need to check the Spine.Event e parameter for its Name. (or more optimally, its Data reference).
    • This is useful for when you have to play sounds according to points the animation like footsteps. It can also be used to synchronize or signal non-Spine systems according to Spine animations, such as Unity particle systems or spawning separate effects, or even game logic such as timing when to fire bullets (if you really want to).
    • During a transition/mixing out, this is fired depending on the TrackEntry.EventThreshold. See "Events During Mixing" below.

At the junction where an animation completes playback, and a queued animation will start, the events are raised in this order: Complete, End, Start.

WARNING: NEVER subscribe to End with a method that calls SetAnimation. Since End is raised when an animation is interrupted, and SetAnimation interrupts any existing animation, this will cause an infinite recursion of End->Handle>SetAnimation->End->Handle->SetAnimation, causing Unity to freeze until a stack overflow happens.

Events During Mixing

The standard AnimationState implementation treats events differently when it does mixing.

When you have a mix time set (or Default Mix on your Skeleton Data Asset), there is a span of time where the next animation starts being mixed with an increasing alpha, and the previous animation is still being applied to the skeleton.

During this crossfade, user events are raised according to the mix/transition percent and the TrackEntry's EventThreshold.

When the mix percentage (mixTime / mixDuration) is less than the eventThreshold, event timelines for the animation being mixed out will be applied. Defaults to 0, so event timelines are not applied for an animation being mixed out.

Sample Code

Here is a sample MonoBehaviour that subscribes to AnimationState's events. Read the comments to see what's going on.

using UnityEngine;
using System.Collections;
using Spine;
using Spine.Unity;

// Add this to the same GameObject as your SkeletonAnimation
public class MySpineControllerThing : MonoBehaviour {

   // The [SpineEvent] attribute makes the inspector for this MonoBehaviour
   // draw the field as a dropdown list of existing event names in your SkeletonData.
   [SpineEvent] public string footstepEventName = "footstep";

   void Start () {
      var skeletonAnimation = GetComponent<SkeletonAnimation>();
      if (skeletonAnimation == null) return;   // told you to add this to SkeletonAnimation's GameObject.

      // This is how you subscribe via a declared method. The method needs the correct signature.
      skeletonAnimation.state.Event += HandleEvent;

      skeletonAnimation.state.Start += delegate (Spine.TrackEntry entry) {
         // You can also use an anonymous delegate.
         Debug.Log(string.Format("track {0} started a new animation.", entry.TrackIndex));

      skeletonAnimation.state.End += delegate {
         // ... or choose to ignore its parameters.
         Debug.Log("An animation ended!");

   void HandleEvent (Spine.TrackEntry entry, Spine.Event e) {
      // Play some sound if the event named "footstep" fired.
      if (e.Data.Name == footstepEventName) {         
         Debug.Log("Play a footstep sound!");


Since the Spine runtimes are source-available and fully modifiable in your project, you can of-course define and raise your own events in AnimationState or in whatever version of it you make.