Search code examples
javascripttypescriptgame-developmentphaser-frameworkphaserjs

Phaser3 TileMap(.json) Multiple Level


Right now my game only restarts on the same tilemap. level-1 tilemap is loaded even if the key is level-2.

What do you think is the best way to make multiple levels?

I tried something like this but "level-2" does not work, tilemap does not update.

Does it make sense to make a new stage for each level? I tried that but it didn't work because I create the player in game.ts and I had problems importing it. Can't I solve this by restarting the scene.

game.ts :

import { Scene } from "phaser";

import Scenes from "./";
import { Player } from "../objects";

import GameManager from "../managers/gameManager";
import Level1Scene from "./level1scene";

export default class GameScene extends Scene {
  //actors
  private declare player: Player;
  private declare currentScene: Scene;
  // tilemap
  private declare map: Phaser.Tilemaps.Tilemap;

  // tilesets
  private declare propsTileSet: Phaser.Tilemaps.Tileset;
  private declare wallTileset: Phaser.Tilemaps.Tileset;
  private declare bgTileset: Phaser.Tilemaps.Tileset;
  // tilemap layers
  private declare propsLayer: Phaser.Tilemaps.TilemapLayer;
  private declare bgLayer: Phaser.Tilemaps.TilemapLayer;
  private declare wallsLayer: Phaser.Tilemaps.TilemapLayer;

  private declare currentMap: string;

  constructor() {
    super({
      key: Scenes.GameScene.key,
    });
  }

  init() {
    this.game.events.emit("react__onGameStart");
  }

  create() {
    this.createTiles();
    this.createLayers();
    this.map.setCollision([1025, 1026]);
    this.createPlayer();
    this.createFullscreenButton();
    this.cameras.main.startFollow(this.player);
    this.cameras.main.setZoom(2);
  }

  private createFullscreenButton() {
    const fullscreenButton = this.add
      .sprite(this.cameras.main.width, this.cameras.main.height, "fullScreen")
      .setInteractive();

    fullscreenButton.on("pointerup", () => {
      this.toggleFullscreen();
    });
  }

  private toggleFullscreen() {
    if (this.scale.isFullscreen) {
      this.scale.stopFullscreen();
    } else {
      this.scale.startFullscreen();
    }
  }

  update(time: number, delta: number): void {
    this.player.update();
    this.physics.collide(this.player, this.wallsLayer);
    this.physics.collide(this.player, this.propsLayer);
    this.physics.add.collider(this.propsLayer, this.player, (player, tile) => {
      if ((tile as Phaser.Tilemaps.Tile).index == 37) {
        console.log("Collision with tile index 37");
        GameManager.instance.nextlevel();
        GameManager.instance.currentLevelKey;
        this.scene.restart();
      }
    });
  }
  private createTiles(): void {
    this.map = this.make.tilemap({
      key: GameManager.instance.currentLevelKey,
    });
    this.propsTileSet = this.map.addTilesetImage("props")!;
    this.wallTileset = this.map.addTilesetImage("ground")!;
  }

  private createLayers() {
    this.add.image(0, 0, "bg").setOrigin(0, 0);
    this.propsLayer = this.map.createLayer("props", this.propsTileSet, 0, 0)!;
    this.wallsLayer = this.map.createLayer("ground", this.wallTileset, 0, 0)!;

    this.propsLayer.setCollisionBetween(36, 40);
  }
  private createPlayer(): void {
    this.player = new Player({
      key: "player",
      scale: 1,
      scene: this,
      tag: "player",
      texture: "atlas",
      x: 50,
      y: 0,
      afterCreate: () => {
        console.log("Player is created");
      },
    });
  }
}

gameManager :

import { Scene } from "phaser";
import { Player } from "../objects";
import Scenes from "../scenes";

export default class GameManager {
  getScene(currentLevelKey: string) {
    throw new Error("Method not implemented.");
  }
  private static _instance: GameManager;
  private player: Player | null;
  private declare levels: string[];
  private declare _currentLevelKey: string;
  private declare currentLevel: integer;
  private constructor() {
    this.player = null;
    this.currentLevel = 1;
    this.levels = ["level-1", "level-2"];
  }

  public static get instance(): GameManager {
    if (!GameManager._instance) {
      GameManager._instance = new GameManager();
    }
    return GameManager._instance;
  }

  public setPlayer(player: Player): void {
    this.player = player;
  }

  public getPlayer(): Player | null {
    return this.player;
  }

  public get currentLevelKey(): string {
    return this.levels[this.currentLevel];
  }

  public nextlevel() {
    if (this.currentLevel < this.levels.length - 1) {
      this.currentLevel += 1;
    }
  }
}


Solution

  • Your code looks good, the only thing that doesn't seem to fit is this.currentLevel = 1; in the constructor, if you want the first level to be called you would have to set this.currentLevel = 0;

    And you need to move this line(s) into the create function

    this.physics.add.collider(this.propsLayer, this.player, (player, tile) => {
      if ((tile as Phaser.Tilemaps.Tile).index == 37) {
        console.log("Collision with tile index 37");
        GameManager.instance.nextlevel();
        GameManager.instance.currentLevelKey;
        this.scene.restart();
      }
    }); 
    

    because this.physics.add.collider add's an eventHandler action, this should be done only once. The update function is called multiple times.

    Tipp: if you create a minimal viable example, it might be easier to debug. here a short guide from StackOverlfow, that might help.