Next event only after an specific animation is complete

Hey guys, that’s my first post in the forum and i’ve searched for this solution but none seems to be quite well what i want.

So… I’ve doing some sprites animation for the character jump but the problem is that the character dont wait the animation to be complete to “setVelocityY(-300);” so he jumps before the animation is done.

This is my code:

if (cursors.up.isDown && player.body.touching.down){
     player.anims.play('jump');
     // expect some delay function
     player.setVelocityY(-300);
}

I’ve already tried something like this:

if (cursors.up.isDown && player.body.touching.down){
    player.anims.play('jump');
    player.once('completeanimation', ()=>{ 
        console.log('animationcomplete')
        player.jump();
    });
}

function jump (){
   player.setVelocityY(-300);
}

But it doesn’t work and the animation keep stuck in the first frame.

Can anyone help me? Thanks!

If you just want to add a delay, you can do this:

if (cursors.up.isDown && player.body.touching.down) {
  player.anims.play('jump')
  scene.time.addEvent({
    delay: 500, // in ms
    callback: () => {
      player.setVelocityY(-300)
    }
  })
}
1 Like

Actually if you just want to wait for the animation to finish you are pointing in the right direction, but the problem, I believe, is with the spelling, the event name is not ‘completeanimation’ but ‘animationcomplete’, so

player.once('animationcomplete', ()=>{ 
        console.log('animationcomplete')
        player.jump();
    });

should work.

2 Likes

Another solution could be to add a SPRITE_ANIMATION_KEY_COMPLETE event to your specific animation key:

// Define your animation keys as constants or variables to prevent typos ;)
const ANIM_JUMP = 'player-jump';

// Create the animation in your scene
this.anims.create({
			key: ANIM_JUMP,
			frames: animationManager.generateFrameNames('textureKey', {
				prefix: 'jump_',
				start: 1,
				end: 10,
			}),
			frameRate: 15,
			repeat: 0,
		});

// Add a listener to the complete-key event, this event will be triggered 
// only when your jump animation completes
player.on(Phaser.Animations.Events.SPRITE_ANIMATION_KEY_COMPLETE + ANIM_JUMP, function(animation, frame) {
        // Add vertical force
	player.setVelocityY(-300);			
}, this);

// Then on your update() method:
function update() {
  // ...  
  if (cursors.up.isDown && player.body.touching.down) {
      // Just trigger the animation, when it completes it will perform the jump.
       player.anims.play(ANIM_JUMP); 
  }
}

You could even wait for a specific frame in the animation to perform the jump with the SPRITE_ANIMATION_KEY_UPDATE event:

player.on(Phaser.Animations.Events.SPRITE_ANIMATION_KEY_UPDATE + ANIM_JUMP, function(animation, frame, gameObject) {
  console.log(frame.index);

  if(frame.index === 4) {
     // Add vertical force
     player.setVelocityY(-300);	
  }
}, this);

1 Like

Uow, thanks jack, this solution is mind blowing and gonna help in so many other problems that i have. But im having some issues with this code:

player.on(Phaser.Animations.Events.SPRITE_ANIMATION_KEY_COMPLETE + ANIM_JUMP, function(animation, frame) {
    // Add vertical force
    player.setVelocityY(-300);			
}, this);

The console is returning the error:
Uncaught TypeError: Cannot read property 'Events' of undefined

Anyway, this is what i need and thanks, just have to do it works now haha

Maybe you know what might be causing this error?

@motylera what version of Phaser are you using?

Phaser event constants were introduced in 3.16.2 I think. And animation key events were introduced in 3.15 https://phaser.io/phaser3/devlog/134

Anyway, it’s just a string, you can replace that with ‘animationcomplete-’ + ANIM_JUMP or just use ‘animationcomplete-player-jump’.

You could also do:

player.on('animationcomplete', function(animation, frame) {
    if(animation.key === 'player-jump') {
       // Add vertical force
       player.setVelocityY(-300);			
    }
}, this);

Same goes for the animation update version, try ‘animationupdate’ and check the animation key inside the event handler. But bare in mind that these generic versions will trigger the event for all animations!

@jackfreak Hey, actually i was using a previous version of Phaser, sorry.
I’ve upgraded to 3.16.2 it and now i have this same error for the two codes you’ve posted:
Uncaught TypeError: Cannot read property 'frame' of undefined

@motylera The code I posted was mostly to give you an idea of what could be done, so errors were expected :). Can you post part of your code? At least the chunks where you create the animation and add the events, and where you trigger the animation inside you update() function.

@jackfreak wow, forget this “frame” error, i’ve messed up and wrote the wrong code on anims.create, forgot the suffix and zeroPad… its pretty basic, shame on me, just wasted 3 hours of my life, but ok.

Now the real question is for some reason the character jumps, do the animation in the air and so that is finished the animation he jumps again, i dont know how but it is executing the player.setVelocityY(-300) two times, making a double jump.

Hey @motylera, great you manage to get it working ! About the double jump, it was expected, when you check on your update() function for the jump key being pressed, it is quite probable that you are triggering the animation several times, your update() is probably running at 60fps so when you press the down key the cursors.up.isDown will run a couple of times in a row (unless your press-and-release skills are super fast :stuck_out_tongue: ).

 // ...  
  if (cursors.up.isDown && player.body.touching.down) {
      // Just trigger the animation, when it completes it will perform the jump.
       player.anims.play(ANIM_JUMP); 
  }

Add a flag to prevent that:

// outside update()
var isJumping = false;

function update() {
 // ...  
    if (cursors.up.isDown && player.body.touching.down && !isJumping) {
         isJumping = true;

        // Just trigger the animation, when it completes it will perform the jump.
         player.anims.play(ANIM_JUMP); 
    }
}


// and reset the flag when the animation completes
player.on(Phaser.Animations.Events.SPRITE_ANIMATION_KEY_COMPLETE + ANIM_JUMP, function(animation, frame) {
    if(isJumping) {
       isJumping = false; 

      // Add vertical force
      player.setVelocityY(-300);			
   }
}, this);

That’s the general idea :slight_smile:

@jackfreak Thank you so much, it works!

1 Like