Thanks for the help!
It's for the Construct plugin, so it triggers a construct function which then calls set animation and triggers another complete callback (so there's not really code to share on the C3 side, but the C3 side calls this other set animation function, so I'll show both of those. Also note that the issue only happen when animation mix is set to non zero. When it's zero, there is no error. The this.trackAnimations was a hack I had to do, so it would be the same animation triggered twice.
This is the listener, the triggers below in the code call functions in C3 events which in turn call setAnimation.
state.tracks[trackIndex].listener = {
complete: (trackEntry, count) => {
this.completeAnimationName = this.trackAnimations[trackEntry.trackIndex];
this.completeTrackIndex = trackEntry.trackIndex;
this.Trigger(C3.Plugins.Gritsenko_Spine.Cnds.OnAnimationFinished);
this.Trigger(C3.Plugins.Gritsenko_Spine.Cnds.OnAnyAnimationFinished);
},
event: (trackEntry, event) => {
this.completeEventName = event.data.name;
this.completeEventTrackIndex = trackEntry.trackIndex;
this.Trigger(C3.Plugins.Gritsenko_Spine.Cnds.OnEvent);
}
Here's set animation, note that the animation state is updated later in the game tick (and if starting at a later time, add the listener after that, so earlier events are not triggered.)
updateCurrentAnimation(loop,start,trackIndex, animationName) {
if (!this.skeletonInfo) return;
if (!this.skeletonInfo.skeleton) return;
if (!this.animationNames) return;
if (!(this.animationNames.includes(animationName)))
{
if (this.debug) console.warn('[Spine] updateCurrentAnimation, animation does not exist.', animationName, this.uid);
return;
}
try {
const state = this.skeletonInfo.state;
const skeleton = this.skeletonInfo.skeleton;
const track = state.tracks[trackIndex];
let currentTime = 0;
let currentRatio = 0;
if (track) {
// calculate ratio and time
currentTime = track.trackTime;
if (track.animationEnd != track.animationStart && track.animationEnd > track.animationStart)
{
currentRatio = (track.animationLast+track.trackTime-track.trackLast)/(track.animationEnd-track.animationStart);
}
}
state.setAnimation(trackIndex, animationName, loop);
switch (start)
{
case 0: break; // Start from beginning
case 1: state.tracks[trackIndex].trackTime = currentTime; break;
case 2: state.tracks[trackIndex].trackTime = currentRatio * (state.tracks[trackIndex].animationEnd-state.tracks[trackIndex].animationStart); break;
default: break;
}
// Record animation assigned for listener
this.trackAnimations[trackIndex] = this.animationName;
if (start == 0 || (start == 2 && currentRatio == 0))
// If starting from beginning or 0 ratio add listners so they'll trigger at 0
{
this.setTrackListeners(state, trackIndex);
} else
// If starting later, apply time, then enable listeners so they do not trigger on past events
{
// state.apply(skeleton);
// skeleton.updateWorldTransform();
this.delayedTrackListeners.push(trackIndex);
// this.setTrackListeners(state, trackIndex);
}
} catch (ex) {
if (this.debug)
{
console.error('[Spine] setAnimation error', ex, trackIndex, animationName);
}
this.spineError = 'setAnimation error '+ex;
this.Trigger(C3.Plugins.Gritsenko_Spine.Cnds.OnError);
}
}