Toggling animations for custom ES6 Classes

Hello!

I am shifting towards using ES6 classes with Javascript in Phaser 3. As of right now, I have a gameScene class, which is where the level information is, and a player class, which stores the player’s animation info and decides which animation to choose, among other things.

The problem arises when I try to make two instances of the player class in gameScene.js. As of right now, the starting animation each player (which are yoshis right now) does is the idle animation. Using the arrow keys triggers the run animation for the second player, and the WASD keys triggers the run animation for the first player. I use the animationcomplete event to determine when the run animation is complete, and then switch back to the idle animation. Two problems emerge:

  1. This isn’t much a biggie, but since I wait until AFTER the run animation is complete to play the idle animation, if I press the movement keys and then let go, the run animation does not immediately stop and switch back to the idle animation. This is a bit of a problem, as I am trying to work towards making a fighting game. I could increase the fps of the run animation, but then the yoshi looks like he’s straight out of a Loony Toons cartoon.

  2. Here’s the frustrating part: For some reason, the second player on the right can run and then switch back to the idle animation. But the first player, located on the left, starts in an idle position. When the movement keys are pressed, the first player goes into the run animation, which it should, but then it can’t go back to the idle animation, being stuck on a frame of the run animation. I’m not sure how exactly I can make the animationcomplete event detect either player’s transition from the run to idle animation. I would also prefer that this event listener be created in the player class.

Attached are some screenshots. Any help is appreciated!

Both are in idle animations:

First player gets “stuck”:

Code for gameScene.js and player.js:gameScene.js (3.8 KB)
player.js (3.1 KB)

Here’s the code in case you can’t view it:
gameScene.js:
var obstacle_bodies;
var yoshi_bodies;
var P1Controls;
var P2Controls;
var background;
var frameCounter;
var obstacles;
var player1;
var player2;

    var GameScene = new Phaser.Class({

	Extends: Phaser.Scene,
initialize:

	function GameScene() {
		Phaser.Scene.call(this, { key: 'gameScene' });
	},

init: function() {
	frameCounter = 0;
},

create: function () {
	P1Controls = this.input.keyboard.addKeys({
		up: 'W',
		down: 'S',
		left: 'A',
		right: 'D'
	});

	P2Controls = this.input.keyboard.addKeys({
		up: 'up',
		down: 'down',
		left: 'left',
		right: 'right'
	});

	this.anims.create(
		{
			key: 'level_background',
			frames: [
				{ key: 'bg1' },
				{ key: 'bg2' },
				{ key: 'bg3' },
				{ key: 'bg4' },
				{ key: 'bg5' },
				{ key: 'bg6' },
				{ key: 'bg7' },
				{ key: 'bg8' },
			],
			frameRate: 10,
			repeat: -1
		});

	this.matter.world.setBounds(0, -100, 1200, 770, 64, true, true, false, true);
	background = this.add.sprite(600, 360, 'bg1').play('level_background');

	obstacle_bodies = this.cache.json.get('obstacle_bodies');
	yoshi_bodies = this.cache.json.get('yoshi_bodies');

	//this.matter.add.sprite(300, -20, 'asheet', 'ball', { shape: shapes.ball })
	player1 = new Player(this, 100, 520, 'yoshi_shapes', 'yoshi_285.png');
	player2 = new Player(this, 1100, 520, 'yoshi_shapes', 'yoshi_01.png', { shape: yoshi_bodies.yoshi_01 }).setFlipX(true);
	//player1.setData({ 'up': P1Controls.A, 'down': P1Controls.S, 'left': P1Controls.A, 'right': P1Controls.D });

	obstacles = this.add.group({
		maxSize: 2,
		removeCallback: () => {
			console.log("Removed");
		}
	});

	/*this.matter.world.on('collisionactive', function (bodyA, bodyB) {
		if (bodyA.label != "Rectangle Body" || bodyB.label != "Rectangle Body")
			console.log("Ground hit!");
		else
			console.log("Yamete!");
})*/
},

update: function () {
	player1.update(P1Controls);
	player2.update(P2Controls);
	frameCounter = frameCounter + 1;
	if ((frameCounter % 125 == 0) && (obstacles.isFull() == false)) {
		var x = Math.floor(Math.random() * 5);
		var obstacle;
		switch (x) {
			case 0:
				obstacle = this.matter.add.sprite(Phaser.Math.Between(300, 900), -20, 'obstacle_shapes', 'ball.png', { shape: obstacle_bodies.ball }).setVelocity(Phaser.Math.Between(-5, 5), Phaser.Math.Between(-30, 30));
				obstacles.add(obstacle);
				break;
			case 1:
				obstacle = this.matter.add.sprite(Phaser.Math.Between(300, 900), -50, 'obstacle_shapes', 'box.png', { shape: obstacle_bodies.box }).setVelocity(Phaser.Math.Between(-5, 5), Phaser.Math.Between(-30, 30));
				obstacles.add(obstacle);
				break;
			case 2:
				obstacle = this.matter.add.sprite(Phaser.Math.Between(300, 900), -50, 'obstacle_shapes', 'manyspike.png', { shape: obstacle_bodies.manyspike }).setVelocity(Phaser.Math.Between(-5, 5), Phaser.Math.Between(-30, 30));
				obstacles.add(obstacle);
				break;
			case 3:
				obstacle = this.matter.add.sprite(Phaser.Math.Between(300, 900), -50, 'obstacle_shapes', 'spike.png', { shape: obstacle_bodies.spike }).setVelocity(Phaser.Math.Between(-5, 5), Phaser.Math.Between(-30, 30));
				obstacles.add(obstacle);
				break;
			case 4:
				obstacle = this.matter.add.sprite(Phaser.Math.Between(300, 900), -50, 'obstacle_shapes', 'spikyball.png', { shape: obstacle_bodies.spikyball }).setVelocity(Phaser.Math.Between(-5, 5), Phaser.Math.Between(-30, 30));
				obstacles.add(obstacle);
				break;
		}
		obstacle.setBounce(1.25);
		
		var timer = this.time.addEvent({
			delay: 6000,
			callback: function () {
				obstacles.killAndHide(obstacle);
				obstacle.destroy();
			},
		})

	}
}

});

export default GameScene;

Code for player.js:
var cursors;
var player;

    class Player extends Phaser.Physics.Matter.Sprite {
    constructor(scene, x, y, texture, frame ) {
        super(scene.matter.world, x, y, texture, frame);
        scene.add.existing(this);
        scene.sys.updateList.add(this);
        scene.sys.displayList.add(this);
        this.sprite = this;
        player = this;
        console.log(this.sprite);
        this.scene = scene;
        this.setPosition(x, y);
        this.setTexture(texture);
        this.setFrame(frame);

        //cursors = scene.input.keyboard.createCursorKeys();
        this.setData("health", 100);

        /*scene.anims.create({
            key: 'jump',
            frameRate: 10,
            frames: scene.anims.generateFrameNames('yoshi_shapes', {
                prefix: 'bjack_',
                suffix: '.png',
                start: 21,
                end: 29,
                zeroPad: 2
            })
        });*/

        scene.anims.create({
            key: 'run',
            frameRate: 7,
            //repeat: -1,
            frames: scene.anims.generateFrameNames('yoshi_shapes', {
                prefix: 'yoshi_',
                suffix: '.png',
                start: 285,
                end: 292,
                zeroPad: 3
            })
        });

        scene.anims.create({
            key: 'idle',
            frameRate: 3,
            repeat: -1,
            frames: scene.anims.generateFrameNames('yoshi_shapes', {
                prefix: 'yoshi_',
                suffix: '.png',
                start: 1,
                end: 6,
                zeroPad: 2
            })
        });

        this.play("idle");

        this.on('animationcomplete', function (animation, frame) {
            //if (animation.key === 'jack-jump') {
            //    console.log("He jumpth");
           // }
            if (animation.key === 'run') {
                console.log("He runth");
                //console.log(this.sprite);
                player.play("idle");
            }
        }, scene);
    }

    jump() {
        if (this.body.position.y > 610) {
            //this.setTexture("jump");
            //this.play('jack-jump');
            this.setVelocityY(-13);
        }
        
    }

    moveLeft() {
        this.x += -7;
        this.setFlipX(true);
        if (this.body.position.y > 610) {
            this.play('run', true);
        }
    }

    moveRight() {
        this.x += 7;
        this.setFlipX(false);
        if (this.body.position.y > 610) {
                this.play('run', true);
        }
    }

    update(controls) {
        if (controls.left.isDown)
            this.moveLeft();
        else if (controls.right.isDown)
            this.moveRight();
        else {
            this.setVelocityX(0);
            //this.play("idlea");
        }

        /*if (Phaser.Input.Keyboard.JustDown(cursors.up)) {
            if (this.body.position.y > 610) {
                this.play('jump');
                this.setVelocityY(-13);
            }

        }*/
    }
}

You can also view the code on my GitHub here, just click on Assignments, then go to Assignment 9 (this was originally for a school project, but the semester ended):
My Github

You can test it here, by clicking on the link to Digital Assignment 9:
Play Here

I appreciate any help can get! Sorry for the long post :blush:

Welp, the Phaser 3 community is not being helpful :slightly_frowning_face:

Here’s the solution, which I found by going to the API:

The sprite.on(‘animationcomplete’, listener) passes in as parameters the animation, frame, and gameObject. So all I did was change my code in the create function of the player.js to:
this.on(‘animationcomplete’, function (animation, frame, gameObject) {
if (animation.key === ‘run’) {
console.log(“He runth”);
gameObject.play(“idle”);
}
}, scene);
It’s disheartening when other posts get a lot more views than yours. Must be because it was a long post.