Life of a Frontend Engineer

Devlog Phaser.js MMORPG | Part 1: create a map with Tiled

May 18, 2020

During the COVID crysis, I decided to take a new challenge: create a MMORPG with javascript. I had plenty of time, and building a game was something that had been running around in my head for quite some time.

I decided to share my journey through the development of my MMORPG with javascript (well, Typescript actually): Alkito.

Is it a Devlog, is it a tutorial ? Both, probably.

This is what have been done so far. You can check the Github repo here: https://github.com/Colmea/alkito-js-mmorpg:

Alkito screenshot

So, ready for this ? Let’s dive in !

The Stack™

Knowing I’m a frontend developer, I decided to use this stack for my MMORPG:

  • Javascript (Typescript) for the Game Core.
  • Phaser.js for the Game engine.
  • React.js for the Game UI.
  • node.js for the server.

We’ll definitely come back to each of these tech later.

Day 1: God Created the Earth (with Tiled)

First thing first, I needed a map for my game. There are many ways to manage this, but the easiest is probably Tiled.

Tiled is an open-source “tiled” map editor. It will allow us to “paint” a map with tiles (dirt, sand, grass, walls, …) and export it to be used in our game.

There are a lot of tutorials on the web to create a map with Tiled, but here are the steps:

  1. Create a new map (I chose a 32px*32px size for each tile).
  2. Import a Tileset image (you can find plenty on the internet, but choose one with 32px tiles).
  3. Create a layer ground and paint ground tiles.
  4. Create a layer objects and paint additional object tiles.

You should have something like this: Tiled screenshot

  1. Export the map in JSON (File > Export As… > Type: JSON map files (.JSON)).

You should now have a JSON map, and a image tileset (the one you imported in Tiled).

Import map in Phaser.js

Fortunately, Phaser.js supports Tiled JSON map out of the box ! We first need to load the JSON map as a tilemapTiledJSON, and the tileset as a spritesheet:

// This is our main scene
export default class WorldScene extends Phaser.Scene {
  constructor() {
    super('WorldScene');
  }

  preload() {
    // load JSON map
    this.load.tilemapTiledJSON('map', 'assets/my-map.json');
    // load tileset image
    this.load.image('myTileset', 'assets/tileset.png');
  }

  create() {}
}

To create the map, we then need to create a tilemap, link the tileset to this map, then create our two layers ground and objects.

// This is our main scene
export default class WorldScene extends Phaser.Scene {
  ...

  create() {
    // Create the map. 'map' is the key of the resource created in the preload
    const map = this.make.tilemap({ key: 'map' });

    // link the tileset to the map.
    // - 'tileset' refers to the tileset name used in the JSON map.
    // - 'myTileset' is the image's key loaded in Phaser.js
    const tileset = this.map.addTilesetImage('tileset', 'myTileset');

    // create the ground layer with our tileset
    const mapLayer = this.map.createStaticLayer('ground', tileset);
    // create the objects layer
    const mapLayer = this.map.createStaticLayer('objects', tileset);
  }
}

You should now have something like this in your browser: Tiled map in Phaser.js

Let us make man

Ok, enough with biblical reference.

To finalize this environment, we need a player. Again, we will use a Sprite Sheet. You can easily create one online with this awesome tool: Online Character Generator.

Click on “Save Result As PNG” at the bottom of the screen to download your sprite sheet.

Player Spritesheet

/!\ Spritesheets from this tool are 64*64px, you should resize them to 32*32px to fit our 32px tiles.

We just need to import this spritesheet, then make the camera follows our player:

preload() {
  ...

  // Load player spritesheet. I set them to 32px because I resized it
  this.load.spritesheet('player', 'assets/player-spritesheet.png', { frameWidth: 32, frameHeight: 32 });
}

create() {
  ...

  // Create the Player from our sprite sheet
  // - 100 are the X and Y position
  // - 'player' is the key of the spritesheet
  // - 31 is the frame I want to use in our spritesheet (i.e. the 31th tile)
  this.player = this.add.sprite(100, 100, 'player', 31);
}

And tada ! Player in Phaser Map

Move our player

Next step, we need to find a way to move our player on the map. I started with the most simple gameplay: move with keyboard.


create() {
  ...

  // Enable physics for our player
  this.physics.world.enable(this.player);

  // Setup camera to follows our player within the map
  this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
  this.cameras.main.startFollow(this.player);

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

update() {
  // playerBody is the physic body attached to our player
  const playerBody= this.player.body;
  const speed = 50;

  // on each update, we detect if one of our key is down
  // and update the player's velocity accordingly
  if (this.keys.up.isDown)
    playerBody.setVelocityY(-speed);
  else if (this.keys.down.isDown)
    playerBody.setVelocityY(speed);
  else if (this.keys.left.isDown)
    playerBody.setVelocityX(-speed);
  else if (this.keys.right.isDown)
    playerBody.setVelocityX(speed);
  else
    playerBody.setVelocity(0);
}

We now have a player, moving with our keyboard, and a camera which follows it.

Player move in Phaser map

Conclusion

There is still a lot to do: add animations, add collisions with objects, …

But you’ll need to wait for the next Devlog ;)

Cheers !


© 2020 Arthur Schwaiger. built with Gatsby