2

I'm very new to Phaser 3 and I've been trying to create an instance where the player sprite collides with a game object that takes the player to another map. Right now I'm trying to make the player change scenes from house 1 to house 2, but when I move the player sprite over the game object nothing happens. No errors come up telling me something isn't defined or anything. It's as if the trigger for changing the scene never existed.

Here's the Player.js file

export default class Player {
  constructor(scene, x, y) {
    this.scene = scene;

    const anims = scene.anims;
        anims.create({
            key: 'turn1',
            frames: [ { key: 'dude', frame: 32 } ],
            frameRate: 20
        });
        anims.create({
            key: 'rdown',
            frames: anims.generateFrameNumbers('dude', { start: 0, end: 7 }),
            frameRate: 16,
            repeat: -1
        });
        anims.create({
            key: 'rright',
            frames: anims.generateFrameNumbers('dude', { frames: [ 8, 9, 10, 11, 12, 13, 14, 15 ] }),
            frameRate: 16,
            repeat: -1
        });
        anims.create({
            key: 'rup',
            frames: anims.generateFrameNumbers('dude', { frames: [ 16, 17, 18, 19, 20, 21, 21, 23 ] }),
            frameRate: 16,
            repeat: -1
        });
        anims.create({
            key: 'rleft',
            frames: anims.generateFrameNumbers('dude', { frames: [ 24, 25, 26, 27, 28, 29, 30, 31 ] }),
            frameRate: 16,
            repeat: -1
        });

    this.sprite = scene.physics.add.sprite(x, y, "dude", 0).setSize(16, 16).setOffset(0, 8);

    this.sprite.anims.play("rdown");

    this.gamepad = scene.input.gamepad.once('down', function (pad, button, index) {
        this.gamepad = pad;
    }, this);
  }

  update() {
    const gamepad = this.gamepad;
    const sprite = this.sprite;
    const speed = 90;
    const prevVelocity = sprite.body.velocity.clone();
    sprite.body.setVelocity(0);
    if (gamepad.right && gamepad.up) {
      sprite.body.setVelocityX(speed);
      sprite.body.setVelocityY(-speed);
    } else if (gamepad.right && gamepad.down) {
      sprite.body.setVelocityX(speed);
      sprite.body.setVelocityY(speed);
    } else if (gamepad.left && gamepad.up) {
      sprite.body.setVelocityX(-speed);
      sprite.body.setVelocityY(-speed);
    } else if (gamepad.left && gamepad.down) {
      sprite.body.setVelocityX(-speed);
      sprite.body.setVelocityY(speed);
    } else if (gamepad.left) {
      sprite.body.setVelocityX(-speed);
    } else if (gamepad.right) {
      sprite.body.setVelocityX(speed);
    } else if (gamepad.up) {
      sprite.body.setVelocityY(-speed);
    } else if (gamepad.down) {
      sprite.body.setVelocityY(speed);
    }
    sprite.body.velocity.normalize().scale(speed);
    if (gamepad.left) {
      sprite.anims.play("rleft", true);
    } else if (gamepad.left && gamepad.down) {
      sprite.anims.play("rleft", true);
    } else if (gamepad.left && gamepad.up) {
      sprite.anims.play("rleft", true);
    } else if (gamepad.right && gamepad.down) {
      sprite.anims.play("rright", true);
    } else if (gamepad.right && gamepad.up) {
      sprite.anims.play("rright", true);
    } else if (gamepad.up) {
      sprite.anims.play("rup", true);
    } else if (gamepad.right) {
      sprite.anims.play("rright", true);
    } else if (gamepad.down) {
      sprite.anims.play("rdown", true);
    } else {
      sprite.stopOnFrame(sprite.anims.currentAnim.getFrameAt(0))
    }
  }
}

Here's the TestLevel.js file

import Player from "./Player.js";
import TestRoom2 from "./TestRoom2.js";

export default class TestRoom extends Phaser.Scene {
  map;
  player;
    constructor() {
        super()
    }
    
    text;
    
    preload() {
        this.load.image('tiles', 'assets/tilemaps/tiles/house1.png');
        this.load.spritesheet('dude', 'assets/images/link.png', { frameWidth: 16, frameHeight: 24 });
        this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/TestRoom.json');
        this.load.image('exit', 'assets/tilemaps/tiles/exit.png');
    }
create()
{
    this.map = this.make.tilemap({ key: 'map' });
    this.map.landscape = this.map.addTilesetImage('house', 'tiles');
    this.map.createLayer("ground", [this.map.landscape], 0, 0);
    this.player = new Player(this, 128, 112);
    this.map.createLayer("above", [this.map.landscape], 0, 0);
    this.cameras.main.setSize(256,224);
    this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
    this.cameras.main.startFollow(this.player.sprite);
    this.cameras.main.setDeadzone(4,4);
    this.exited = this.physics.add.sprite(112, 220, 'exit').setOrigin(0,0);
    this.exitBox= this.physics.add.group({
            key: 'exit'});
    this.physics.add.collider(this.player, this.exitBox, function(player, exitBox) { this.scene.start('TestRoom2')});
  }
update() {
this.player.update();
    this.physics.collide(this.player, this.exitBox, function(player, exitBox) { this.scene.start('TestRoom2')});
    
  }
}

The other map is the same, but with TestRoom replacing TestRoom2.

Here's the main scene that has the game config

import TestRoom from "./TestRoom.js";
import TestRoom2 from "./TestRoom2.js";

var config = {
    type: Phaser.AUTO,
    width: 256,
    height: 224,
    backgroundColor: '#000000',
    pixelArt: true,
    input: {
      gamepad: true
    },
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 0 },
            debug: true
        }
    },
    scene: TestRoom, TestRoom2
};
var game = new Phaser.Game(config);

Everything works. The only thing that doesn't is initiating the scene change.

What can I do to fix this?

winner_joiner
  • 12,173
  • 4
  • 36
  • 61
  • Did my answer solve your problem, or did I miss something? If your question is solved please consider accepting my answer. – winner_joiner Apr 19 '23 at 08:08
  • Yes and no, I still had the problem with the sprite not colliding with the exited object. But I did manage to find a way to make it work somehow. I just used a static platform group like the one used in the Phaser 3 tutorial. But your answer did help me clean up my code a bit. – Kenzie Sanderson Apr 19 '23 at 17:06
  • Now I see it I think the reason why the player doesn't collide, is because it is a basic javascript `class`. for the collision to work you need to access the **sprite** of the player, with this line of code `this.physics.add.collider(this.player.sprite, this.exited, (player, exited) => { this.scene.start('TestRoom2')});` it should work. – winner_joiner Apr 19 '23 at 19:42

1 Answers1

1

First the config is not 100% correct, all scenes have to be in an array ( or be added later manually ). Here you are missing square brackets [, ]

var config = {
    type: Phaser.AUTO,
    width: 256,
    height: 224,
    ...
    scene: [TestRoom, TestRoom2] // <-- missing brackets
};

Next, you can remove the following line, from the update function, the collider in the create function is enough:

// remove this line from "update" function
this.physics.collide(this.player, this.exitBox, function(player, exitBox) { this.scene.start('TestRoom2')});

In the create function, alter the following line to pass the context for the callback function (Link to documentation):

// added to parameters ( for details check documnetation )
this.physics.add.collider(this.player, this.exitBox, function(player, exitBox) { this.scene.start('TestRoom2')}, null, this);

Or use a arrow function (link to documentation):

// altered with to an arrow function
this.physics.add.collider(this.player, this.exitBox, (player, exitBox) => { this.scene.start('TestRoom2')});

I think this should cover all errors I could find, by reading the code.

btw.: check the browser console for errors, this can help finding and solving problems.

Update:

I think the line in the create function should be exited instead of exitBox, since a empty group will never collide with the player:

this.physics.add.collider(this.player, this.exited, (player, exited) => { this.scene.start('TestRoom2')});

You could checkout the example of this answer to see a short working example of player/scene switching.

Update:

Based on the comments, you can fixe th issue of not colliding, is using the following line of code.

The reason is that the Player class is not a Phaser GameObject, but a simple javascript class. You can use the sprite property of the Player class.

this.physics.add.collider(this.player.sprite, this.exited, (player, exited) => { this.scene.start('TestRoom2')});
winner_joiner
  • 12,173
  • 4
  • 36
  • 61