• Runtimes
  • Complete and End not working as intended

Start, Complete and End are Spine.AnimationState events, used by the SkeletonAnimation class.

SkeletonAnimator is different; it doesn't use Spine.AnimationState at all. It currently uses Unity's SendMessage API and not Spine-C#'s C# events. I don't think you even get Start/End/Complete with SkeletonAnimator.

When using either SkeletonAnimation or SkeletonAnimator, weird things happen inside the duration of a mixing/crossfading transition if you are using that. Some events don't fire in a consistent or expected manner. This applies to all runtimes. It is most likely that Events from the previous animation are ignored. Using SkeletonAnimator/the Unity API events are much more confusing. It's not Spine's fault. Unity's Animation system's event firing is faulty and unpredictable.

I notice that End actually doesn't fire after each loop though. Thanks for reporting this.
As for whether or not this is "intended" behavior, that's really up to Nate (or up to you if you want to modify the code to suit your needs). Complete and End are pretty ambiguously similar terms.

If you need need help editing the code for events for your purposes, just try to describe it in this post and we'll try to see what should be done.

Related Discussions
...

I'm sorry for the typo, it's SkeletonAnimation throughout.
I managed to work around this after trial and error. It seems, that Complete only fires if all queued animations are finished.
Which means that:

  • End fires every time an animation concludes, except a loop iteration
  • Complete fires every time an animation form the queue is completed, interrupted OR in the end of each loop cycle.

My understanding is, that there's always one animation active at any time, especially for mixing to work (nobody wants a frozen character). That's why I think firing Complete on any successfully completed animation, by ending/ mixing into the next would makes sense.

I think events are working perfectly fine EXCEPT for where mixing is involved. Even user-defined events don't work properly.
So yeah, I agree with you when you say it makes sense for Complete to fire when one animation plays to its full duration then a new animation starts playing. It not firing there during crossfading is a bug that exists in all runtimes right now.

So did you manage a workaround or did you only figure out how it currently works?

Right now I kind of have a solution, but there's still something that might get a problem in the future:

Calling SetAnimation (for an enter animation) and AddAnimation (for an eventual idle transition, looped) on Game Start, only calls END when enter is finished.
Then SetAnimation (for an exit animation) and AddAnimation (for an eventual idle transition, looped), instantly ENDs the idle from before and only calls COMPLETE when the exit animation is finished.
Because of this, I have to listen to END in the first situation and to COMPLETE in the second. Since the animations both end and start in opposite directions off screen, I cannot have a mixed transition, because otherwise you'll see the object moving across the screen. I usually run a randomiser here, to get another random SkeletonAnimation picked for the next enter animation (starting all over again). BUT if that's the same Object as before, not the mix from exit to enter is used but from that additional idle to enter (maybe because listening to COMPLETE already mixed into the idle?). They're also random so I have to set that from all idles to enter myself. I'm hoping that there won't be any random enters in the future, otherwise that's getting messy.
END would not even be an option because as you said in another post, SetAnimation on END doesn't make sense (though it does in this case, if END would trigger SetAnimation at a moment where the exit animation mix is still used) and is not even fired though it should.

It's just not reasonable for me, why the exact same Add-/ SetAnimation calls result in END being called if it's my enter/ idle combination and COMPLETE being called if it's my exit/ idle combination... Doesn't make sense. Even happens when starting the game with any animation already set on the SkeletonAnimation.

Sorry for the long explanation, but I'm just not sure how to deal with that correctly.

And, is there even another way to always keep at least one animation running, by means of not calling AddAnimation with an idle for every Animation that's about to finish?

P.S.: BUG: When AddAnimation is called with looped = true, the bool in the inspector does not get set during the run of that animation.

I'll try to read through that later. :p
I think just fixing Spine-C# for your project would be the way to go though, instead of dancing around Spine-C#'s bugs.
The issue isn't that it's hard to fix it for your specific purposes. I think the issue is fixing it so that it applies to general cases. Nate hasn't mentioned if he has made any decisions on this front yet, because it's not just about the Spine-C# runtime. It's about all the runtimes.

I will say though, SkeletonAnimation's inspector's animation name and loop fields are pretty crappy.
But it has partly to do with Spine-Unity's deep guts being just Spine-C#.

3 أشهر لاحقا

Hey Pharan,

thanks for the quick reply.
Well... I try to avoid changing a runtime that might get updated and I'm not sure if I even understand it enough to change.
I think the long post is obsolete now, this one is more precise.
I narrowed the problem down a bit further.
It seems that AddAnimation makes a huge difference. I changed my code so I:

  • SetAnimation(Enter Animation)
  • when COMPLETE, AddAnimation (Idle Animation, looped)
  • SetAnimation(Exit Animation)
  • when COMPLETE, change SkeletonAnimation and start over again

This works perfectly, I get COMPLETE and END calls in both cases this time. HOWEVERm, there's an issue on the last line:
On COMPLETE of Exit, I AddAnimation (Idle Animation, looped), and SetAnimation(Enter Animation), IN THAT ORDER.
The result is that Exit and Enter are immediately ENDed and the idle loop is the current animation being played. If I ignore the AddAnimation call, it's all fine.

This might be a bug, that first ADD and then SET Animation will override the set animation with the added one and play that.


I wanted to kind of reopen this post, because I still have this problem. I'll describe the steps in order:

  • SetAnimation (Exit)
  • COMPLETE (Exit) -> AddAnimation (Idle)
  • COMPLETE (Exit) -> SetAnimation (Enter)
  • END (Exit)
  • START (Enter)
  • END (Enter)
  • START (Idle)

If I comment out AddAnimation(Idle) everything works fine. Is it possible that calling AddAnimation and SetAnimation one after another is mixing things up? I don't understand the execution chain after SetAnimation (Enter)... it looks as if the added "Idle" animation gets played after the END of "Exit" which it was added upon, immediately cancelling out the newly set "Enter" animation.
If it's me doing something wrong here, please tell me, but I am not doing anything else but always adding Idle Animations if there's none in the queue and in this case calling SetAnimation(Enter) on COMPLETE of "Exit". The rest is happening all by itself.

2 أعوام لاحقا

I realize this is an older thread but thought I'd post in case it helped someone else along the road. I've also had the issues with inconsistent animation events with spine in Unity. Particularly event handlers like these in Unity c#

   [code]skeletonAnimation.state.End += delegate (Spine.TrackEntry entry)
    {
        Debug.Log("End event fired for track: " + entry.TrackIndex);
    };

    skeletonAnimation.state.Start += delegate (Spine.TrackEntry entry)
    {
        Debug.Log("Start event fired for track: "+entry.TrackIndex);
    };[/code]

They are inconsistent and undependable, especially for layered animations. I agree with alex.seeck on avoiding changing the spine runtimes so that you don't run into update conflicts with future releases, and don't have to spend time maintaining your changes within each future release. However Esoteric has done an excellent job with custom animation events. My workaround that solved these issues for me was only using custom events with the Event handler instead of Start or End. For example:

    [code]skeletonAnimation.state.Event += delegate (Spine.TrackEntry entry, Spine.Event e)
    {
        Debug.Log("Custom event fired for track: " + entry.TrackIndex + " with event name: " + e.Data.Name);
    };[/code]

Custom events with the event handler have proven to be very accurate and reliable in my experience even in animations with multiple layers and tracks. It always returns the right event with the right trackindex at the right time. So in Spine itself I've added custom events for "Start" and "End" and keyed them respectively at the beginning and end of each of my animations. While I also do not think this solution is ideal, start and end events should come for free in the runtimes, in its current state, this is the cleanest solution I could come up with while avoiding editing the runtimes. Hope this helps someone 🙂

Since this thread started, we've made some big changes to event handling in the Spine runtime. They should be 100% reliable. If that's not the case for you, you may have run into a bug. Could you please provide us with a reproduction case that shows the issue?

21 أيام لاحقا

Am using Spine on my first commercial project and have hit a problem with the end state trigger. I'm using the following to turn off an object when a specific anim finishes

plr.state.End += delegate
{
   // *** DELEGATE ***
   if (plr.state.GetCurrent(0).Animation == exploreSwapOutAnim)
   {
      gameObject.SetActive(false);
   }
};

This does get triggered but I think it's when the 0.2s mix time is reached rather than the end of the anim. If I change this default to 0 then it immediately triggers and I see none of the animation. This also happened when I tried using the specific animation mixing values entries. I just updated to the latest and it doesn't seem to be any different (Last updated: UTC - 2017 March 16).

I think you may be running into the new default of non-looping animations not being cleared unless you clear them yourself or explicitly set the duration.
If you want the old default of non-looping animations automatically clearing after they finish once, you can find this line and use the commented out value: spine-runtimes/AnimationState.cs at master

current default:

entry.trackEnd = float.MaxValue; // don't clear the track even if the animation doesn't loop.

old default:

entry.trackEnd = loop ? float.MaxValue : animation.Duration; // don't clear looping animations. clear non-looping animations after it reaches Duration.

@Pharan Afraid that hasn't solved it. The object also has 3 other layered animations on it but I want to check against the finishing of the layer 0 anim. Is this correct for that? The other layers are looping so shouldn't end so I'm guessing this end event must be from the base animation? If I set the time to 1s then the whole animation plays but I now get the blending I don't want and the animation looks poor.

Ah, I see. My bad.

I'm not sure about what your setup is and what you were expecting.
Can you maybe describe what the sequence of animations is on the base layer/track 0 in a table? And when you expected End to fire? You know the difference between End and Interrupt, right? Are you on the latest Spine and Spine runtime?

Your first post above vaguely sounds like it's doing what it's supposed to be doing, but I'm not sure.

I have a multi-layered anim and I want to trigger something when a specific animation on a specific layer finishes playing.

5 أعوام لاحقا

I'm having the same issue, I have several animations in tracks and I want to trigger something when a specific animation on a specific track finishes playing.

I'm doing:

baseTrack.Complete += onComplete;

But it's not being called. This happens on track 4, while track 0 is still playing a loop.
I tried what was suggested to enable clearing of the tracks, but it didn't make any difference.

I also tried SetAnimation and AddAnimation, but no difference.

The onComplete method is never called. How can I debug this?
I can send a very simple repro project to you if it helps, but i'd rather not share it here.

Thanks!

This thread was started 7 years ago, not sure you have the same problem! Please show your setAnimation and addAnimation calls, as well as adding your listener.

Thanks for the quick response! 😃

I did some laboration with mixDuration but didn't notice any difference.
But the code is very simple. Anything here out of the common?

Also it's worth noticing that i'm playing looping animations on track 0-3, this one is on track 4, with loop set to false.


baseTrack = skeletonAnimation.state.SetAnimation(track, anim, loop);
baseTrack.Complete += OnAttackAnimComplete;

// Have this as well after, not sure if it matters?
skeletonAnimation.Update(0); // Update skeleton so it happens now, and not next frame (prevents flicker)

...

// This is never triggered
private void OnAttackAnimComplete(TrackEntry trackEntry) {
   Debug.Log(Time.time + "<color=white>: OnAttackAnimComplete " + trackEntry.Animation.Name + "</color>");
}


Sorry, it seems to be something in my code setting the animation multiple times, so it's most likely no spine issue. Thanks for the help 🙂

Thanks very much for the followup, glad you've figured it out! 🙂