• Runtimes
  • [spine-c] 3.5, skeleton resets after track ends all anims

  • संपादित
Related Discussions
...

In previous C runtime versions skeletons always stayed in the exact same state (I mean bones transformations, etc..) they were left after their animation(s) has ended. And that was pretty useful (i.e. I could call setAnimation a few updates later, and not right away upon getting "complete" event, and during that time - picture on the screen stays consistent).

After the recent update though skeletons started to reset themselves right after track ends it's last animation. And I don't even sure if that was an intended change in the logic.

It was also useful in cases where I need to set animation timeline to its end and take the final bones positions or some other info. Now I have to set trackTime to trackEnd * 0.99 (which is not entirely correct) instead of 1.0, otherwise I get wrong values;

Yeah, I'm also interested to leave animation at last frame. Is it possible?

This is working as intended.
If you want to hold the last pose, set trackEnd to positive infinity or a maximum type value.

Hm, but I need complete event + keep last position. If I set trackEnd to infinity, do I get complete event?

Judging from the code, you should get the Complete event.
spine-runtimes/AnimationState.c at master

AnimationState.c

if (entry->loop ? (trackLastWrapped > FMOD(entry->trackTime, duration))
            : (animationTime >= animationEnd && entry->animationLast < animationEnd)) {
   _spEventQueue_complete(internal->queue, entry);
}

AnimationState.java

// Queue complete if completed a loop iteration or the animation.
if (entry.loop ? (trackLastWrapped > entry.trackTime % duration)
   : (animationTime >= animationEnd && entry.animationLast < animationEnd)) {
   queue.complete(entry);
}

For looping animations we default trackEnd to essentially forever. For non-looping we default it to the animation duration. Maybe it would make sense to use the same default for both looping and non-looping? That would mean by default animations continue being applied, even if non-looping, and if you want the animation to stop being applied you have to explicitly set trackEnd or play another animation. Thoughts?

Looks good for my side.

We have made the change so trackEnd defaults to forever for both looping and non-looping animations. This will make it to Git master for all runtimes soon.

I just tried the latest version from master but for some reason AnimationStateListener's end method isn't being called [libgdx]

That's because animations continue being applied until a new animation is set or the track is cleared. See TrackEntry trackEnd.

You could also listener for complete instead of end.

Sorry if AnimationState functionality feels like moving target, but we felt the trackEnd default value change was worth it. The sooner the less overall pain it causes. We don't have plans for any other changes in 3.5 that affect how the API is used.

Yes I completely understand and agree that if aggressive API changes need to be made, the sooner the better.

Thanks for the complete vs end tip, that might solve it all 🙂

Nate लिखा

We have made the change so trackEnd defaults to forever for both looping and non-looping animations. This will make it to Git master for all runtimes soon.

Does this now mean that attachment stuff over multiple tracks will now work.

i.e.
SetAnimation("attachment on",0,false) //999 frames
AddAnimation("attachment Off",1,0.5/delay/, false)// 1 frame

attachment off will continue to be applied. iirc you suggested that I needed to set the track end time to #inf

Yes, that should work now.

एक वर्ष बाद में
  • संपादित
Nate लिखा

That's because animations continue being applied until a new animation is set or the track is cleared.

I was updating some legacy code to the 3.6 runtime, and my End Listeners stop working. By comparing with my previous version, I found out that in the AnimationState.c update function I had this

/* End non-looping animation when it reaches its end time and there is no next entry. */
if (!current->loop && current->lastTime >= current->endTime) spAnimationState_clearTrack(self, i);

and now there's this

/* Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. */
if (current->trackLast >= current->trackEnd && current->mixingFrom == 0) {

I understand the changes made in 3.5 to trackEnd, but not how to get the same I had before. In order to keep the previous pose after the animation, trackEnd should be kept at INT_MAX, but then how should the track be cleared in order to fire the End Event?

If TrackEntry trackEnd is used to keep applying the animation after it is completed (which is the default behavior), then AnimationStateListener end isn't quite what you want. You probably want AnimationStateListener complete.

Thanks for the quick response.

Quickly trying to use AnimationStateListener complete instead of end in some cases, resulted in a better result, but still not the same as before the update. Probably more changes would be needed in other functions, which we probable can't make now.

However, I think I am missing the right usage for the End Listeners then (and how to use them, since trackEnd doesn't let the track being cleared), and how to accomplish the same behaviour as before.

If you want trackEnd to clear the track when the animation duration is reached:

TrackEntry entry = animationState.addAnimation(...);
entry.trackEnd = entry.animation.duration;

This was the old behavior for non-looping animations. The default was changed to match looping animations, which continue being applied. There was discussion and the reasoning was good, though I don't remember it off the top of my head.

If you want the animation to continue being applied past its duration, that is the default behavior but end won't fire, as you found, because end means the track entry is done and won't be applied again.

Ok, last question :p

Is it possible to keep the last pose after the animation duration is reached, other then by having the trackEnd set to infinite?
In other words, can I have both end event and keep last position.

When trackEnd is reached, the track is cleared. When the track is cleared, the animation will no longer be applied, so end occurs. Instead of using trackEnd you could clear the track yourself at the right time.

See exactly when each event occurs here:
AnimationStateListener
If you don't want to use trackEnd to clear the track, then the end event isn't what you want.

Again, you can leave trackEnd so the animation continues being applied, then listen for complete which for a non-looping animation happens once when the animation duration is reached.