Skip to content

Tilemap

The tilemap system loads Tiled maps (.tmj / .json) and renders tile layers with automatic camera culling, per-layer tint/opacity, parallax scrolling, and collision generation.

Quick Start

The simplest way to use tilemaps is through the editor:

  1. Export your map from Tiled as JSON (.tmj)
  2. Place the .tmj file and tileset images in your project assets folder
  3. Add a Tilemap component to an entity and set the Source to your .tmj file

The scene loader automatically parses the map, loads tileset textures via the AssetServer, and registers everything for rendering.

Components

Tilemap

A marker component that references a Tiled map source file. The scene loader reads this field to preload map data and tileset textures before the scene is instantiated.

PropertyTypeDefaultDescription
sourcestring''Path to the .tmj / .json map file

TilemapLayer

Describes a single tile layer for rendering. Each visible layer in a Tiled map creates one entity with this component.

PropertyTypeDefaultDescription
widthnumber10Layer width in tiles
heightnumber10Layer height in tiles
tileWidthnumber32Tile width in pixels
tileHeightnumber32Tile height in pixels
texturenumber0Tileset texture handle
tilesetColumnsnumber1Number of columns in the tileset image
layernumber0Sort layer index
tilesnumber[][]Flat array of tile IDs (row-major)
tintColor{r:1,g:1,b:1,a:1}Per-layer tint color
opacitynumber1Layer opacity (0–1)
visiblebooleantrueWhether the layer is rendered
parallaxFactorVec2{x:1,y:1}Parallax scroll factor

How It Works

Scene Loading Pipeline

When a scene contains an entity with a Tilemap component, the scene loader:

  1. Reads the source field (e.g., maps/level1.tmj)
  2. Loads the JSON via the AssetServer and parses it with parseTmjJson
  3. Resolves tileset image paths relative to the .tmj file
  4. Loads tileset textures via AssetServer.loadTexture() and registers their dimensions
  5. Caches the parsed map data with registerTilemapSource()

At runtime, the TilemapPlugin reads the cache and submits visible layers for rendering each frame.

Build Targets

The tilemap asset pipeline works across all build targets:

  • Editor preview: Loads via AssetServer over the preview HTTP server
  • WeChat MiniGame / Playable Ads: Loads via RuntimeAssetProvider using the bundled assets
  • Web build: Same as editor preview, assets served from the build output

Programmatic Loading

For cases where you need to load a tilemap from code (outside the scene loader), use the lower-level APIs:

import { parseTmjJson, loadTiledMap, registerTextureDimensions } from 'esengine';
// Parse Tiled JSON data
const mapData = parseTmjJson(jsonObject);
// Load tileset textures through the AssetServer
const textureHandles = new Map<string, number>();
for (const tileset of mapData.tilesets) {
const info = await assetServer.loadTexture(tileset.image);
textureHandles.set(tileset.image, info.handle);
registerTextureDimensions(info.handle, info.width, info.height);
}
// Spawn tilemap layer entities into the world
const entities = loadTiledMap(world, mapData, textureHandles);

C++ Parser (WASM)

For maps with external tilesets (.tsj files), use parseTiledMap which delegates to the C++ parser:

import { parseTiledMap } from 'esengine';
const mapData = await parseTiledMap(jsonString, async (source) => {
// Resolve external tileset files
return await assetServer.loadText(source);
});

loadTiledMap Options

OptionTypeDefaultDescription
generateObjectCollisionbooleantrueCreate colliders from Tiled object layers
collisionTileIdsnumber[]auto-detectedTile IDs that should generate collision bodies

Collision Generation

Object Layer Collision

Tiled object layers are automatically converted to physics bodies. Supported shapes:

  • RectangleBoxCollider with matching half-extents
  • EllipseCircleCollider with the larger axis as radius
  • Polygon / PolylineBoxCollider fitted to the bounding box
  • Point → skipped

Each collision object creates a static RigidBody entity with the appropriate collider.

Tile Collision

Mark tiles as collidable in Tiled by adding a custom property collision = true to the tile in the tileset. The loader reads these IDs and generates merged collision rectangles:

const entities = loadTiledMap(world, mapData, textureHandles, {
collisionTileIds: [1, 2, 3],
});

Collision Merging

Adjacent collision tiles are automatically merged into larger rectangles to reduce physics body count. The algorithm scans row by row, expanding each rectangle rightward and downward as far as possible:

Before merge: [1][1][1] After merge: [ 1 ]
[1][1][1] [ 1 ]

This produces fewer, larger BoxCollider bodies for better physics performance.

TilemapAPI

Low-level API for runtime tile manipulation:

MethodDescription
TilemapAPI.initLayer(entity, w, h, tw, th)Initialize a tile layer for an entity
TilemapAPI.destroyLayer(entity)Destroy a tile layer
TilemapAPI.setTile(entity, x, y, tileId)Set a single tile
TilemapAPI.getTile(entity, x, y)Get a tile ID
TilemapAPI.fillRect(entity, x, y, w, h, tileId)Fill a rectangle with a tile
TilemapAPI.setTiles(entity, tiles)Set all tiles from a Uint16Array
TilemapAPI.hasLayer(entity)Check if an entity has an initialized layer

Editor Workflow

  1. Add a Tilemap component to an entity in the Hierarchy
  2. Set the Source field to a .tmj or .json Tiled map file using the asset picker
  3. The editor automatically loads the map, resolves tileset textures, and renders the layers
  4. Collision objects are visualized as gizmo overlays

Layer Properties

Tiled layer properties map directly to TilemapLayer fields:

Tiled PropertyTilemapLayer FieldDescription
opacityopacityLayer transparency
tintcolortintHex color string (#AARRGGBB or #RRGGBB)
parallaxxparallaxFactor.xHorizontal parallax factor
parallaxyparallaxFactor.yVertical parallax factor
visiblevisibleLayer visibility

Next Steps