Skip to content

Tilemaps

samme edited this page Nov 4, 2024 · 32 revisions

There is a good tutorial series, Modular Game Worlds in Phaser 3:

  • Static Maps — tilesets, map from array, map from CSV, map from Tiled JSON, collisions with Arcade Physics
  • Dynamic Platformer — painting tiles, converting tiles to sprites, Arcade Physics hitboxes
  • Procedural Dungeon — blank layers, fill tiles, put tiles at
  • Meet Matter.js — Matter Physics, convert tilemap layer, collision shapes

Summary

The basic procedure is:

  1. Load tileset images
  2. Load a tilemap file, if using one
  3. Create a tilemap object
  4. Assign tileset images to the tilemap
  5. Create tilemap layers, specifying one or more tilesets for each
  6. Mark colliding tiles in a layer, if you want collisions
  7. Add a collider for a tile layer, if you want collisions

Using Tiled

New map

  • Map size must be fixed.

Tilesets

  • Use only a Tileset Image, not an Image Collection.
  • If you don't embed the tileset here, you'll need to do it when exporting.

JSON export

  • Embed tilesets
  • Detach templates
  • Resolve object types and properties

Alternatives to Tiled

I find Tiled pretty painful to use.

Tileset images

Every tilemap layer needs at least one tileset image to render. You assign this to the map with addTilesetImage().

load.image() is fine for this, but you can also use load.spritesheet() (with the appropriate frame dimensions) if you want to use the tileset image as a multiframe texture for other game objects like sprites.

If you get any errors loading images, fix those first before creating the tilemap.

Empty tilemap

Tilemap from array

Tilemap from CSV

Tilemap from Tiled

Create the tilemap from the loaded map data:

const tilemap = this.add.tilemap('map');

If you've forgotten the tile layer or tileset names, you can find them there:

console.log('tile layers', tilemap.layers);
console.log('tilesets', tilemap.tilesets);

For each tileset, add an image:

const tileset = tilemap.addTilesetImage('name', 'textureKey');

For each layer, create a layer with at least one tileset:

const tileLayer = tilemap.createLayer('tileLayerName', tileset);

The lazy way to create tilemap layers:

for (const { name } of tilemap.layers) {
  tilemap.createLayer(name, tilemap.tilesets);
}

Collisions with Arcade Physics

collide() and add.collider() test only the tile IDs or locations set by setCollision() etc., setTileIndexCallback(), and setTileLocationCallback(). For the last two, your callback can return false to cancel separation with the tile.

overlap() and add.overlap() test all intersecting tiles, including empty tiles. You can filter these out pretty easily if you need to.

this.physics.add.overlap(
  sprite,
  tilemapLayer,
  function onOverlap(sprite, tile) {
    console.log("overlap", tile.x, tile.y);
  },
  function process(sprite, tile) {
    // Tile not empty
    return tile.index > -1;
  }
);

Object layers

Check your object properties

const map = this.add.tilemap(/*…*/);

const [firstObjectLayer] = map.objects;

console.info('Object properties in %s:', firstObjectLayer.name);
console.table(firstObjectLayer.objects);

console.info('Object custom properties in %s:', firstObjectLayer.name);
console.table(firstObjectLayer.objects.map(o => o.properties));

Collisions with Matter Physics

Collision shapes

Ghost collisions

"Ghost collisions" can happen in physics engines when two colliding bodies are next to one another, e.g. a player trying to walk over two neighboring ground tiles. The order in which the collisions are resolved by the engine can cause "unrealistic" effects, e.g. the player being stopping dead in their tracks on flat ground. See http://www.iforce2d.net/b2dtut/ghost-vertices for more info.

When working with tilemaps and Matter, there are a couple ways to mitigate this issue:

  • Add chamfer to bodies, i.e. round the edges, or use circular bodies to reduce the impact of the ghost collisions.
  • Map out your level's hitboxes as as a few convex hulls instead of giving each tile a separate body. You can still use Tiled for this. > Create an object layer, and fill it with shapes, convert those shapes to Matter bodies in Phaser (see below).
  • Use a library like hull.js to automatically figure out convex hulls from your tiles.

tilemap/collision/matter ghost collisions

Performance

  • Reduce the number of visible layers
  • Reduce the number of visible tiles in the viewport (e.g., don't zoom out)
  • Reduce the number of cameras (e.g., no minimaps)

For small static maps, you can draw tilemap layers onto a Render Texture (maybe 4096 × 4096 at largest) and then hide or destroy the originals.

Clone this wiki locally