EsotericSoftware / spine-runtimes

2D skeletal animation runtimes for Spine.
http://esotericsoftware.com/
Other
4.35k stars 2.89k forks source link

[c] Incomplete/ambiguous state event lifecycle #1916

Closed AbePralle closed 3 years ago

AbePralle commented 3 years ago

Using latest spine-c runtime: 490ca75

My spine character has states idle_1, idle_2, etc.

I set a single top-level listener on the animation state and I log out all events.

I play a randomly selected non-looping idle state with e.g.:

spAnimationState_setAnimationByName( animation_state, 0, "idle_1", 0 );

The events I get are:

There is no END event even though the animation is non-looping.

If I play idle_2 immediately after seeing COMPLETE then I get:

This is irregular, but the core issue is that if my engine randomly chooses to play idle_1 twice in a row, it becomes difficult to track whether the animation is still playing or not.

My events if I replay idle_1 upon seeing the first idle_1 complete:

Replaying idle_1 interrupting the previous play (before seeing COMPLETE):

Bottom line is that a completed single play should report END even if nothing else is queued. Currently it only reports END once another state starts;

badlogic commented 3 years ago

This may seem counter intuitive at first, but the END event is actually fired correctly. Have a look at the docs: http://en.esotericsoftware.com/spine-api-reference#AnimationStateListener-end

Let's have a look at the first sequence: START idle_1 COMPLETE idle_1

While the idle animation has completed, i.e. it ran from its first to last frame, the animation's last frame will still be applied every time you call spAnimationState_apply(), making sure your skeleton stays in the pose of the last frame (possibly mixed with animations on other tracks).

If the entry on that track for that idle animation would be dequeued, your skeleton would revert to its setup pose. Not something you generally want.

The END event is fired when the track entry for that animation is no longer the current track entry and about to be disposed. This means its effect on the pose will also be gone. That only happens if another track entry replaces it, like in your other example sequences.

You can always check if the animation you queued is still playing by comparing its playback time with the animation duration, i.e.:

spTrackEntry *entry = spAnimationState_getCurrent(animationState, 0);
float duration = entry->animationEnd - entry->animationStart;
int isComplete = duration < entry->trackTime; // track time is the time this entry has been enqueued so far.
AbePralle commented 3 years ago

That makes sense, thank you!