• Runtimes
  • Problems transitioning animations using JS widget

Related Discussions
...

I am having an issue where I want a widget to play a second animation after it finishes playing the first animation.

widget.setAnimation("Born",
{
   complete:function(entry){
      widget.setAnimation("Looping");
   }
});

For some reason doing the above code will display a messed up frame of that doesn't exist in either animation? Even if I force the second animation to play the same animation as the first, it still plays an unknown frame?

The puff of smoke separates the 2 animations being played HOWEVER there is no puff of smoke in either animation? There is smoke in other animations though, just not the ones I am currently playing?

However, calling setAnimation inside a setTimeout will not show the unknown smoke frame but even when I set the timeout to 0, it will still play a second of the previous animation before switching.

widget.setAnimation("Born",
{
   complete:function(entry){
      setTimeout(function(){ 
         widget.setAnimation("Looping"); 
      },0);
   }
});

Animation 1: The creature falls from a puff of smoke. Animation 2: the creature stands idle. However, there is a noticeable jump cut where you see the first frame of animation 1 playing (the creature in the air) before animation 2 plays (the creature standing idle) This makes the creature look like they jumped back up in the air for a fraction of a second.

Both solutions look very bad in my game. I have around 40 creatures each with 4 - 5 animations and they all look bad when transitioning from one animation to the next...

Is the smoke frame that shows in the first example how it looks in the setup pose?

Often code looks like:

state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
// draw skeleton

Note that the event and complete events are fired in AnimationState apply, so when you set a new animation in response to complete, it won't be applied until the next frame. My guess is Born completes, resetting the skeleton to the setup pose, Looping is set as the current animation but not applied, drawing happens and you see the setup pose, the next frame Looping is applied and you see it.

AnimationState queues events and only fires them after its processing is complete (right before apply returns), so it is safe to set new animations, call apply again, or otherwise change AnimationState state from the event callbacks.

To solve your problem, you could set your first animation to looping, so it continues to play for an extra frame before Looping is applied. Alternatively, you can try:

widget.setAnimation("Born", {
   complete:function(entry) {
      widget.setAnimation("Looping");
      widget.state.apply();
   }
});

I'll add a note to the documentation that event and complete events are fired in apply.

This event is trigged in AnimationState apply, so any animations that are set won't be applied until the next time the AnimationState is applied.

Thanks for the speedy reply! But I am still having trouble...

I am not sure if the smoke is from the setup pose as we hired a freelance artist that only gave us the exports. I am trying to import the json and atlas into spine with no luck. I do an import data and all the images are missing. I pointed the Images path to where my images are and it found them but hitting refresh doesn't show the images either.

I guess I will have to assume that you are correct in that the smoke is part of the setup pose.

I tried the code you gave me

widget.setAnimation("Born", {
   complete:function(entry) {
      widget.setAnimation("Looping");
      widget.state.apply();
   }
});

But it gives me the error

AnimationState.ts:137 Uncaught Error: skeleton cannot be null.
    at AnimationState.apply (AnimationState.ts:137)
    at Object.complete (index.htm:118)
    at EventQueue.drain (AnimationState.ts:754)
    at AnimationState.apply (AnimationState.ts:189)
    at SpineWidget.render (Widget.ts:210)
    at Widget.ts:241

Also, I don't follow you with this part

you could set your first animation to looping, so it continues to play for an extra frame before Looping is applied

I would need to see the attachment names and what is under your Images node in the tree. Images are found like this:
Images - Spine User Guide: Image file lookup
If all you have is an atlas, you'll need to unpack it to individual files:
Texture Packing - Spine User Guide: Texture Unpacker

Ah, you need to pass the skeleton: widget.state.apply(widget.skeleton);

For the other idea, you need to set your animation so it loops. You can set looping on the widget config widget.config.loop = true; or use an HTML attribute data-loop="true". Or, you can use AnimationState instead of widget.setAnimation. For looping the first animation:

var entry = widget.state.setAnimation(0, "Born", true); // true means looping
entry.listener = {
   complete: function(entry) { // called once Born has completed one loop
      widget.state.setAnimation(0, "Looping", true);
   }
};

For not looping the first animation:

var entry = widget.state.setAnimation(0, "Born", false); // false means not looping
entry.listener = {
   complete: function(entry) { // called once Born has completed one loop
      widget.state.setAnimation(0, "Looping", true);
      widget.state.apply(widget.skeleton); // Apply the new Looping animation now.
   }
};

Thank you very much! That works perfectly!!

Edit:

Also, Once I used the texture unpacker, I could instantly see the images in Spine again! And yes, the smoke was in the Setup pose!