javascriptcollision-detectionphaser-frameworkscenephaserjs

Player sprite isn't interacting with game object to change maps


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?


Solution

  • 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')});