0
\$\begingroup\$

I'm starting to develop in Phaser 3, and I'm really a begginer.

I'm trying to make an attack move of an archer sprite, but I cannot make it work right...

This is how I have defined the things related to combat:

class Jugador extends Phaser.Physics.Arcade.Sprite {

    constructor(escena, x, y) {
        super(escena, x, y, "jugador");
        this.escena = escena;
        this.escena.add.existing(this);
        this.escena.physics.add.existing(this);

        this.createVariables();
        this.createControls();
        this.createAnimations();
  }
  
  createAnimations(){
    this.defineAttackAnimation();
    /// Some others...
  }

  defineAttackAnimation(){
    this.attackAnimation = {};
    this.attackAnimation.key = "attack";
    this.attackAnimation.frames =
      this.escena.anims.generateFrameNames("attack",
      {
        prefix: "attack",
        start: 1,
        end: 6
      });
    this.attackAnimation.frameRate = this.attackFrameRate;
    this.attackAnimation.repeat = 1;
    var atkListener = () => {
      this.arrow();
    };
    this.escena.anims.create(this.attackAnimation);
    this.escena.anims.get("attack")
      .addListener("animationcomplete", atkListener);
  }

  arrow(){
    console.log("arrow");
  }

  attack(){
    if(this.body.onFloor() && this.attackButton.isDown){
      this.setVelocityX(0);
      this.setVelocityY(0);
      this.play("attack", true);
    }
  }

  update() {
    if ( this.body.onFloor() ){
      this.setVelocityY(0);
    }

    this.facing();
    this.idle();
    this.walk();
    this.jump();
    this.attack();

    if (this.body.velocity.y < 0){
      this.play("jump");
    } else if (this.body.velocity.y > 0 && !this.body.onFloor()){
      this.play("falling");
    }

  }
}

What I want to do is:

  1. Player pushes the attack button.
  2. Attack animation plays. I would like it to be played untill it ends, but now I have to keep pushing the attack button.
  3. When attack animation ends, the function "arrow" is executes in order to make an arrow appear.

Thanks in advance.

\$\endgroup\$
0
\$\begingroup\$

using the animationcomplete event is doable for you scenario, however StateMachine probably be better depending on how complicated your animation is. this is a good article on how to create one: https://www.mkelly.me/blog/phaser-finite-state-machine/

Below is a copy of my extended version from above to do complicated attack combo chaining. but also is super useful for going between states i.e JUMPING, IDLE, RUNNING, ROLLING etc

  • enter, exit will only be called once
  • execute() is an update loop called in your scene update() method
  • and depends on your event, im expecting my animationcomplete event to be called multiple times, otherwise you can just use .once() instead of .on so you dont need to remove the event listener with .off
  • take note on how i use the event listener .once and .on in the enter(). how .off is used in exit(). and how im continue to listen to isDOWN during the update() loop
  • the idea of below code is, when user hit attack once from IDLE state it will come to Attack1State, during this state, user might still holding the key (isDOWN), so you are expecting the user to let go and trigger .on key up event, but limiting it to only one time event preventing multiple presses ( ATTACK.once() ). On the 2nd isDown during update() loops is where i check other conditions, it if match transition to another state. if the user did not press another ATTACK button before the animation ends than we send the object back to IDLE state and reset the counter. after animation ends. I also given a tiny window of buffer to allow user press it again with the delay

note: it is written in typescript

    8 class Attack1State extends State {
    9     ANIMATION_END: boolean = false
   10     enter({ref, stateData}) {
   11         var { ATTACK } = ref.KEYS
   12         ref.play("hero_attack1_animation")
   13         ref.body.setVelocityX(0)
   14        
   15         ATTACK.once('up', ()=>{
   16             stateData.ATTACK_COUNT++
   17         }) 
   18        
   19         ref.on('animationcomplete', this._animationEnd, this)
   20     }  
   21     excecute({ref, stateData}) {
   22         var { ATTACK } = ref.KEYS
   23         if (ATTACK.isDown && stateData.ATTACK_COUNT == 1 && this.ANIMATION_END && ['idle','move'].includes(this.stateMachine.prev_state)) {
   24             this.stateMachine.transition('attack2')
   25         }
   26     }  
   27     exit({ref}) {
   28         ref.off('animationcomplete', this._animationEnd, this)
   29     }  
   30     private _animationEnd(animation) {
   31         var self = this
   32         if (animation.key !== 'hero_attack1_animation') return
   33         this.ANIMATION_END = true
   34         this.stateMachine.stateArgs.scene.time.addEvent({
   35             delay: 500,
   36             callback:() => {                                                                                                                          
   37                 if (this.stateMachine.stateArgs.stateData.ATTACK_COUNT == 1) {
   38                     self.stateMachine.transition('idle')
   39                     this.stateMachine.stateArgs.stateData.ATTACK_COUNT = 0
   40                 } 
   41         }})
   42     }  
   43 }      
   44    
\$\endgroup\$
0
\$\begingroup\$

don't use addlistener on the animation you can either

1) use on on your game object (Sprite)

this.on('animationcomplete', this.arrow, this);

https://phaser.io/examples/v3/view/animation/on-complete-callback

or

2) use the .chain function

https://phaser.io/examples/v3/view/animation/chained-animation

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.