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 updefs(unknown = solid).defs?: Record<number, TileDef>— Per index: a slope or one-way line. Defaultgame.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'whenforeground— 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 indata) 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. Aforegroundflag 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, sonamecannot 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 withmap.layer(id). Default: the world layer's name.data: number[][]— rows × cols of tile indices: 0 is empty, N drawsfirstFrame + N - 1.firstFrame: number— The atlas frame of tile 1 (whataddStrip/addGrid/loadImagereturned).distance?: number— Parallax: the layer scrolls atcamera / 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 (#rrggbbor#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 ownid— a level file's'ground'— falling back to the world layer's name. Throwstiles: 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? Whatgrid-moveasks. 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 — whatrig.bounds()takes so the camera never shows past its edge.kill(): void— Kill the layers and bodies and drop the map frommaps. 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 readstypeand 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.


