Skip to content

Tilemaps ​

A TileLayer draws a whole grid of tiles as one batch. Use it instead of one Sprite per cell: a 200x200 map is 40,000 cells, and as sprites that is 40,000 nodes.

A tile layer ​

ts
import { Texture, TextureRegion, TileLayer } from "willow";

class TileDemo {
    static main(): void {
        const tiles: Texture = Texture.load("assets/tileset.png");
        const regions: Array<TextureRegion> = TextureRegion.grid(tiles, 16, 16);

        const COLS: int = 50;
        const ROWS: int = 38;
        const data: Array<int> = [];
        for (let row: int = 0; row < ROWS; row++) {
            for (let col: int = 0; col < COLS; col++) {
                const edge: boolean = row === 0 || col === 0 || row === ROWS - 1 || col === COLS - 1;
                data.push(edge ? 2 : 1);
            }
        }

        const ground: TileLayer = new TileLayer({
            tileWidth: 16, tileHeight: 16, columns: COLS, rows: ROWS,
            tileset: tiles, regions: regions, data: data,
        });
        engine.root2d.add(ground);

        ground.setTile(10, 10, TileLayer.encodeGid(3, true));
    }
}

GIDs. Each cell holds a number. 0 is empty; N draws regions[N - 1]. The top bits carry the horizontal, vertical and diagonal flip flags used by the Tiled editor. Use TileLayer.encodeGid(index, flipH, flipV, flipD) and TileLayer.decodeGid(gid) rather than packing bits yourself.

MethodDoes
setTile(col, row, gid)Changes one cell.
getTile(col, row)Reads one cell, with any tile animation applied.
setData(array)Replaces the whole grid. The length must be columns * rows.
setAnimation(gid, frames, fps)Animates every cell showing gid through a list of GIDs, like water or torches.
query()A read-only TileQuery for collision and pathfinding.

Out-of-range cells raise an error rather than being ignored.

Culling big maps ​

A bare TileLayer draws its whole grid. Put it under a TilemapViewport and only the tiles inside the camera view (plus a margin) are drawn each frame, so a huge map stays cheap:

ts
import { Texture, TextureRegion, TileLayer, TilemapViewport } from "willow";

class BigMap {
    static main(): void {
        const tiles: Texture = Texture.load("assets/tileset.png");
        const data: Array<int> = [];
        for (let i: int = 0; i < 512 * 512; i++) {
            data.push(1 + (i % 7));
        }

        const view: TilemapViewport = new TilemapViewport({ padding: 64 });
        engine.root2d.add(view);

        const ground: TileLayer = new TileLayer({
            tileWidth: 16, tileHeight: 16, columns: 512, rows: 512,
            tileset: tiles, regions: TextureRegion.grid(tiles, 16, 16), data: data,
        });
        view.add(ground);
    }
}

padding is the extra margin, in world pixels, drawn beyond the edge of the view.

Querying tiles ​

ts
import { Texture, TextureRegion, TileLayer, TileQuery, Sprite } from "willow";

class Walls {
    static main(): void {
        const tiles: Texture = Texture.load("assets/tileset.png");
        const data: Array<int> = [];
        for (let i: int = 0; i < 20 * 15; i++) {
            data.push(i % 20 === 0 ? 2 : 1);
        }
        const ground: TileLayer = new TileLayer({
            tileWidth: 32, tileHeight: 32, columns: 20, rows: 15,
            tileset: tiles, regions: TextureRegion.grid(tiles, 32, 32), data: data,
        });
        engine.root2d.add(ground);
        const map: TileQuery = ground.query();

        const hero: Sprite = new Sprite("assets/hero.png");
        hero.setPosition(100, 100);
        engine.root2d.add(hero);

        App.onFrame((dt: float): void => {
            let nx: float = hero.x;
            if (Keyboard.isDown(Key.LEFT)) {
                nx -= 150 * dt;
            }
            if (map.tileAtWorld(nx, hero.y) !== 2) {
                hero.x = nx;
            }
        });
    }
}

TileQuery also has tileAt(col, row), rawAt(col, row) (ignoring animation), worldToTile(x, y) and tileToWorld(col, row). Out-of-range reads return 0.

Pair a tilemap with the camera's scrollToTile(col, row, seconds) to move the view to a cell.

Next ​