Tilemap

A tile-grid node: one atlas texture sliced into cells and laid out on a uniform grid, drawn as one node with view culling — the way to build a 2D level from tiles. It holds atlas indices as given; for grass edges, roads and fences picked by rules, paint material ids with Autotile2D; for a level in a .scene2d.ts file, use its tilemap block. Extends Node2D (transform, hierarchy, layer).

  • build a 2D level from a tilemap and a tile atlas
  • lay out a level as rows of tile numbers in code
  • change, add or clear one tile while the game runs
  • place the map in the world
  • turn or mirror a tile without editing the atlas
  • add collision or animated tiles to a tile level

Platforms: iOS, Android, desktop, the web player and the le.codes viewer app; not on the web (Web Lite); headless tests build it but never draw it.

At a glance

TypeScript
async function main() {
  const scene = new Scene2D()
  const tiles = await Texture2D.load(asset('./tiles.png'))

  const map = new Tilemap({
    texture: tiles,
    cols: 6, rows: 3,           // map size in tiles
    tile: 32,                   // each cell 32×32 world units
    atlas: [8, 4],              // the texture holds 8×4 tiles
    data: [                     // row 0 = TOP row; -1 = empty
      -1, -1, -1, -1, 12, -1,
      -1,  0,  1,  2, -1, -1,
       8,  8,  8,  8,  8,  8,
    ],
  })
  map.position = [0, 0]         // the map's BOTTOM-LEFT corner
  scene.add(map)
  scene.open()
}
main()

Create a tile grid from an atlas: new Tilemap

TypeScript
new Tilemap(options: {
  texture: Texture2D
  cols: number                     // map width in tiles
  rows: number                     // map height in tiles
  tile: number | [w, h]            // on-screen cell size in world units (number = square)
  atlas: [atlasCols, atlasRows]    // how the texture is sliced into tiles
  data: number[] | Int32Array      // row-major atlas indices, length cols*rows; -1 (any negative) = empty
})

map.cols, map.rows                 // readonly

The grid is uploaded once; the texture comes from Texture2D.load.

Cell order and where the map sits

  • data is row-major with row 0 at the top of the map (reads like level text in source code), even though world Y points up.
  • Atlas indices also count row-major through the atlas grid: 0 is the atlas's top-left tile.
  • The node's position is the map's bottom-left corner.
  • An index may carry a rotation / mirror (rot90(12), flipX(12), packTile(12, turns, flip)) — the renderer turns that tile; see rotated tiles.

Change or clear one tile at runtime: setTile

TypeScript
map.setTile(x: number, y: number, index: number): this   // grid coords as in data (row 0 = top)

Sets one cell to an atlas index (-1 clears it). Cheap — the grid lives natively. Coordinates outside the grid are ignored.

TypeScript
async function main() {
  const scene = new Scene2D()
  const tiles = await Texture2D.load(asset('./tiles.png'))
  const map = new Tilemap({ texture: tiles, cols: 4, rows: 2, tile: 32, atlas: [8, 4],
    data: [-1, -1, -1, -1, 8, 8, 8, 8] })
  scene.add(map)
  scene.open()

  map.setTile(1, 0, 12)          // put a crate (atlas tile 12) on the top row
  map.setTile(2, 1, -1)          // dig a hole in the ground row
  map.setTile(3, 1, rot90(8))    // the same ground tile, turned a quarter clockwise
}
main()

Remove a tilemap: destroy()

A Tilemap is not collected when you drop it: it lives until destroy() (its own, or an ancestor's).

Collision and animated tiles (not provided)

The tilemap is a static uniform grid. Not provided (as of the current source):

  • animated tiles — swap cells yourself with setTile if needed
  • per-tile collision — build level collision separately, e.g. invisible Node2Ds with Shape2D boxes/segments and static Physics2D bodies

See also

  • Node2D — transform, layer (inherited)
  • Texture2D — loading the atlas
  • Tilesets — autotile materials, oriented tile values
  • Scene files — the tilemap block of a .scene2d.ts
  • 2D physics — static collision geometry for levels
  • Scene2D — layers, Y-sort