Phaser AE

Tilemaps

Free packv3.0.0-32DPhysics

Tilemaps for 2D worlds. Tile layers are drawn from a tileset and culled to the camera, so a huge map costs only what the screen shows, and a separate collision grid becomes a few static bodies with slopes and one-way platforms.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 19, 2026

Tilemaps: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

tiles2d

function tiles2d(): Pack<{ tiles: Tiles2dApi }>

Tilemaps: culled layers as aggregates, collision as merged static bodies. Needs physics-2d composed for shaped bodies and for one-ways that can be passed through; without it the collision spawns plain tagged boxes, a slope tile becomes a full tile box and a one-way a solid 4-unit strip, and a console warning says so once for each of the two kinds a map uses.

Helpers

TILE

const TILE: { readonly slopeNE: 2; readonly slopeSE: 24; readonly slopeNW: 13; readonly slopeSW: 35; readonly oneWayUp: 12; readonly oneWayDown: 23; readonly oneWayRight: 34; readonly oneWayLeft: 45; }

The slope and one-way indices by name, so a hand-built map need not carry magic numbers.

Types

CollisionSpec

interface CollisionSpec

The collision grid: what becomes static bodies.

  • data: number[][] — rows × cols: 0 open, 1 solid, other values look up defs (unknown = solid).
  • defs?: Record<number, TileDef> — Per index: a slope or one-way line. Default game.tiles.defaultDefs, the built-in 48-entry table, indices and all.
  • tags?: string[] — Tags on the bodies, for collision rules. Default ['wall'].

LevelData

interface LevelData

A whole level, as a tile editor exports it.

  • sprites?: LevelSpawnData[] — The things to place.
  • layer: LevelLayerData[] — The tile layers, one of which may be named 'collision'.

LevelLayerData

interface LevelLayerData

ONE LAYER OF A LEVEL FILE, as a tile editor exports it. The layer NAMED 'collision' is the collision grid; every other layer is drawn.

  • name: string — The layer's name. 'collision' makes it the collision grid rather than a drawn layer.
  • tilesize: number — Tile size in world units.
  • data: number[][] — rows × cols of tile indices.
  • firstFrame?: number — The atlas frame index tile 1 maps to. Default 0.
  • repeat?: boolean — Tile it forever in both directions — a looping backdrop. Default false.
  • distance?: number — Parallax divisor: 1 locks to the world, 2 scrolls at half speed. Default 1.
  • foreground?: boolean — Draw it in FRONT of the objects rather than behind. Default false.
  • layer?: string — The world layer to draw into. Default 'world', or 'front' when foreground — name it yourself if your world's layers are named differently.

LevelSpawnData

interface LevelSpawnData

One thing to place when the level loads.

  • type: string — What it is. A NAME, not a class — the game decides what each one means.
  • x: number — World x.
  • y: number — World y.
  • settings?: Record<string, unknown> — Anything else the editor wrote for it.

TileAnim

interface TileAnim

An animated tile: the map's one clock steps through seq.

  • time: number — Seconds per frame.
  • seq: number[] — Tile indices (1-based, as in data) to cycle through.

TileDef

type TileDef = readonly [number, number, number, number, boolean]

A line across a tile in tile-local 0..1: [x1, y1, x2, y2, solid]. solid fills the side to the RIGHT of the directed line; false makes a one-way edge (passable from its open side).

TileLayer

interface TileLayer

A live drawn layer.

  • name: LayerName — The world layer it sits in, LIVE. Moving a layer between world layers is how a map draws IN FRONT of the sprites. A foreground flag could only ever answer "before or after"; a world layer answers "where in the whole stack", which is the question every object in the scene is already answering, so nothing here is special-cased for tiles.
  • readonly id: string — The layer's OWN name — a level file's 'ground', or the world layer's name when the spec did not give one. It is how a layer is found again: several layers draw into one world layer, so name cannot tell them apart.
  • readonly handle: Handle — The one roster object that draws it.
  • readonly cols: number — Columns.
  • readonly rows: number — Rows.
  • get(c: number, r: number): number — The tile index at (c, r); 0 outside the layer.
  • set(c: number, r: number, v: number): void — Set the tile index at (c, r); out of range is ignored. Nothing rebuilds.
  • firstFrame: number — The atlas frame tile 1 maps to, LIVE. A level file carries tile INDICES; which atlas frames those are is a runtime fact the file cannot know, so pointing a loaded layer at a tileset is the normal last step of loading one. Changing it is also how a level swaps its whole palette in one write.
  • distance: number — Parallax divisor, live.
  • repeat: boolean — Repeat forever in both axes, live.
  • animations: Record<number, TileAnim> — The animations, keyed by tile INDEX — live, so a sequence can be retuned or added while the map runs. The animation belongs to the index rather than to any tile, which is why a hundred water tiles shimmer in step and cost one clock and one lookup between them. A tile that must be out of phase with its neighbours is not a tile — it is an object.
  • visible: boolean — Drawn at all, live.
  • readonly count: number — Non-empty tiles.
  • readonly drawn: number — Tiles emitted last frame.

TileLayerSpec

interface TileLayerSpec

One drawn layer of a map.

  • layer: LayerName — The world layer this sits in.
  • id?: string — This layer's own name, for finding it again with map.layer(id). Default: the world layer's name.
  • data: number[][] — rows × cols of tile indices: 0 is empty, N draws firstFrame + N - 1.
  • firstFrame: number — The atlas frame of tile 1 (what addStrip/addGrid/loadImage returned).
  • distance?: number — Parallax: the layer scrolls at camera / distance. 2 = half speed, reads as far away. Default 1.
  • repeat?: boolean — Tile the layer in both axes forever. Default false.
  • tint?: string — A CSS hex colour (#rrggbb or #rrggbbaa) multiplied into every tile. Default #ffffff.
  • visible?: boolean — Drawn at all. Default true.
  • animations?: Record<number, TileAnim> — Animated tiles, keyed by tile index: one clock on the map.

TileMap

interface TileMap

A live map: layers, collision and the bodies it became.

  • readonly tile: number — Tile size, world units.
  • readonly x: number — World x of the top-left.
  • readonly y: number — World y of the top-left.
  • readonly cols: number — Columns.
  • readonly rows: number — Rows.
  • readonly layers: ReadonlyArray<TileLayer> — The drawn layers, in spec order.
  • layer(name: string): TileLayer — A layer by its own id — a level file's 'ground' — falling back to the world layer's name. Throws tiles: no layer "<name>" when there is none.
  • cell(x: number, y: number): { c: number; r: number } — World point → tile column/row (may be outside the map).
  • solid(c: number, r: number): boolean — Is the collision tile at (c, r) anything but open? What grid-move asks. False outside the map or with no collision.
  • collisionAt(c: number, r: number): number — The raw collision value at (c, r); 0 outside the map or with no collision.
  • setCollision(c: number, r: number, v: number): void — Change a collision tile: every body of the map rebuilds. Out of range, no collision, or an unchanged value is ignored.
  • readonly bodies: number — Static bodies the collision became.
  • bounds(): { x: number; y: number; w: number; h: number } — The map's rect in world units — what rig.bounds() takes so the camera never shows past its edge.
  • kill(): void — Kill the layers and bodies and drop the map from maps. Also done when the scene exits.

TileMapSpec

interface TileMapSpec

What tiles.map() takes.

  • id?: string — Prefix for the spawned objects' ids. Default 'tiles'.
  • tile: number — Tile size in world units (square).
  • x?: number — World x of the map's top-left. Default 0.
  • y?: number — World y of the map's top-left. Default 0.
  • layers: TileLayerSpec[] — The drawn layers; all must be the same rows × cols.
  • collision?: CollisionSpec — The collision grid, same rows × cols as the layers. Optional.

Tiles2dApi

interface Tiles2dApi

game.tiles.

  • map(spec: TileMapSpec): TileMap — Build a map. Throws when no layer or collision gives rows and columns, or when a layer's or the collision's size differs from the map's. Scene-scoped.
  • load(data: LevelData, opts?: { id?: string; x?: number; y?: number }): { map: TileMap; spawns: ReadonlyArray<LevelSpawnData> } — Build a map from a LEVEL FILE — the shape a tile editor exports. The layer named 'collision' becomes the collision grid and the rest are drawn, in the order given, with their own parallax and repeat. It returns the SPAWN LIST rather than placing anything. A level file names no class to construct: a level file that can construct arbitrary code is a level file you cannot safely load from anywhere. The game reads type and decides.
  • readonly maps: ReadonlyArray<TileMap> — Every live map.
  • readonly defaultDefs: Record<number, TileDef> — The built-in defs: every ramp angle in four directions plus four one-ways, 48 entries whose numbers never change.