Tilemaps
Tilemaps let you paint 2D levels quickly using a tileset (a grid of sprites) or colored-square tiles.
A Tilemap entity is a regular entity, so you can move, scale, parent,
and duplicate it like anything else.
Quick start
- Right click on the Scene Graph and click New Entity → Tilemap.
- Drag a spritesheet PNG onto Atlas in the Properties Panel.
The editor auto-slices it intoresolution x resolution
squares
(64 x 64 px by default). - Open the Tilemap tab on the bottom panel when the Tilemap entity is selected.
- Pick a tile and start painting.
Tip — scale filtering
Set Scale filter mode to “nearest” for crisp pixel-art,
or “linear” for smooth scaling.
Scripting a Tilemap
Need procedural generation or an in-game brush?
Just cast your entity as a Tilemap and call its helper methods.
import { Behavior, Rng, syncedValue, Tilemap } from "@dreamlab/engine";
/** Pick a random element from the array with the supplied PRNG. */
function sampleArray<T>(array: readonly T[], prng: () => number): T {
const idx = Math.floor(Math.abs(prng()) % 1 * array.length);
return array[idx]!;
}
export default class Generate extends Behavior {
#tilemap = this.entity.cast(Tilemap);
@syncedValue()
seed: number = 0;
/** Grass‑only palette IDs. */
static readonly #GRASS = [1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27] as const;
/** Flower‑only palette IDs. */
static readonly #FLOWER = [4, 5, 6, 7, 12, 13, 14, 20, 21, 22, 23, 28, 29, 30, 31] as const;
/** Deterministic RNG tied to `seed`. */
#prng!: () => number;
#setRng(): void {
this.#prng = Rng.Seeded(BigInt(this.seed));
}
/** Decide which palette to use, then pick a sprite ID from it. */
#getTile(): number {
const roll = this.#prng(); // 0 … 1
if (roll < 0.05) return 0; // empty
if (roll < 0.8) { // grass
return sampleArray(Generate.#GRASS, this.#prng);
}
return sampleArray(Generate.#FLOWER, this.#prng);
}
clearData(): void {
this.#tilemap.clearTiles();
}
generateMap(size = 50): void {
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
this.#tilemap.setTile(x, y, this.#getTile());
}
}
}
onInitialize() {
// Re‑seed whenever the network‑synced seed value changes.
this.values.get("seed")?.onChanged(() => {
this.#setRng();
if (this.game.isServer()) this.generateMap();
});
this.#setRng();
if (this.game.isServer()) this.generateMap();
}
onFrame(): void {
if (!this.game.isClient()) return;
const world = this.inputs.cursor.world;
if (!world) return;
const left = this.inputs.getKey("MouseLeft");
const right = this.inputs.getKey("MouseRight");
if (!left && !right) return;
const tile = this.#tilemap.getTileCoordinatesAtPoint(world);
if (!tile) return;
const id = left ? 33 /* hard‑coded brush */ : this.#getTile();
this.#tilemap.setTile(tile.x, tile.y, id);
}
}
Example — Colored Square Tilemap
If you want a simple colored checkerboard pattern instead of an atlas,
you can set per-tile colors:
import { Behavior, Tilemap } from "@dreamlab/engine";
export default class Checkerboard extends Behavior {
#tm = this.entity.cast(Tilemap);
static readonly WHITE = 0xffffff;
static readonly GREY = 0xcccccc;
static readonly N = 256;
#fill(): void {
this.#tm.clearTiles();
for (let y = 0; y < Checkerboard.N; y++) {
for (let x = 0; x < Checkerboard.N; x++) {
const color = ((x + y) & 1) === 0 ? Checkerboard.WHITE : Checkerboard.GREY;
this.#tm.setColor(x, y, color);
}
}
}
onInitialize(): void {
if (this.game.isServer()) this.#fill();
}
}
Core API
Method | Purpose |
---|---|
getTile(x, y) | Get atlas tile ID at (x, y) , or undefined . |
setTile(x, y, atlasId?) | Set atlas tile ID; pass undefined to erase. |
setTiles(xs[], ys[], atlasIds[]) | Set multiple atlas tiles at once. |
getColor(x, y) | Get solid color at (x, y) , or undefined . |
setColor(x, y, color?) | Set solid color; pass undefined to erase. |
getTileInfo(x, y) | Get TileInfo (either { type: "atlas", id } or { type: "color", color } ). |
setTileInfo(x, y, info?) | Set a full TileInfo object. |
getTileCoordinatesAtPoint(worldPos) | Convert world coordinates to tile (x, y) . |
clearTile(x, y) | Remove any tile (atlas or color) at (x, y) . |
clearTiles() | Remove all tiles. |
tiles() | Iterate all filled tiles. |
bounds (getter) | { width, height, offset } in tile units. |
TileInfo
union
type TileInfo =
| { type: "atlas"; id: number }
| { type: "color"; color: number };
FAQ
How big can a tilemap be?
Technically unlimited, but performance is optimized for ≤ 20 million drawn tiles (similar to large Terraria worlds).
Can I swap atlases at runtime?
Yes — assign a new path to Atlas; visuals update automatically.
How do I force pixel-perfect textures?
Set Scale filter mode to "nearest" on the Tilemap or Camera.