Skip to content

Commit f00abe4

Browse files
committed
add more info to the tilemaps page
1 parent 84399ce commit f00abe4

1 file changed

Lines changed: 145 additions & 2 deletions

File tree

docs/guide/tilemaps.mdx

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,149 @@
1+
---
2+
sidebar_position: 1000
3+
title: Tilemaps
4+
---
5+
16
# Tilemaps
27

3-
Tilemaps allow you to quickly create maps using a predefined tileset.
8+
Tilemaps let you paint 2D levels quickly using a tileset (a grid of sprites).
9+
A **Tilemap** entity is a regular entity, so you can move, scale, parent,
10+
and duplicate it like anything else.
411

5-
## Creating New Tilemap And Assigning Pallete
612
<video src="/img/liltilemapguide.mov" style={{ maxWidth: "100%" }} autoPlay controls></video>
13+
14+
## Quick start
15+
16+
1. **Create → Tilemap** in the scene graph.
17+
2. Drag a spritesheet PNG onto **Atlas** in the Properties Panel.
18+
The editor auto-slices it into `resolution x resolution` squares
19+
(64 x 64 px by default).
20+
3. Open the Tilemap tab on the bottom panel when the Tilemap entity is selected.
21+
4. Pick a tile and start painting.
22+
23+
> **Tip — scale filtering**
24+
> Set **Scale filter mode** to “nearest” for crisp pixel-art,
25+
> or “linear” for smooth scaling.
26+
27+
---
28+
29+
## Scripting a tilemap
30+
31+
Need procedural generation or an in-game brush?
32+
Just ask the entity for its `Tilemap` component and call the helpers.
33+
34+
```ts
35+
import { Behavior, Rng, syncedValue, Tilemap } from "@dreamlab/engine";
36+
37+
/** Pick a random element from the array with the supplied PRNG. */
38+
function sampleArray<T>(array: readonly T[], prng: () => number): T {
39+
const idx = Math.floor(Math.abs(prng()) % 1 * array.length);
40+
return array[idx]!;
41+
}
42+
43+
export default class Generate extends Behavior {
44+
#tilemap = this.entity.cast(Tilemap);
45+
46+
@syncedValue()
47+
seed: number = 0;
48+
49+
/** Grass‑only palette IDs. */
50+
static readonly #GRASS = [1,2,3,8,9,10,11,16,17,18,19,24,25,26,27] as const;
51+
/** Flower‑only palette IDs. */
52+
static readonly #FLOWER = [4,5,6,7,12,13,14,20,21,22,23,28,29,30,31] as const;
53+
54+
55+
/** Deterministic RNG tied to `seed`. */
56+
#prng!: () => number;
57+
58+
#setRng(): void {
59+
this.#prng = Rng.Seeded(BigInt(this.seed));
60+
}
61+
62+
/** Decide which palette to use, then pick a sprite ID from it. */
63+
#getTile(): number {
64+
const roll = this.#prng(); // 0 … 1
65+
if (roll < 0.05) return 0; // empty
66+
if (roll < 0.8) { // grass
67+
return sampleArray(Generate.#GRASS_TILES, this.#prng);
68+
}
69+
return sampleArray(Generate.#FLOWER_TILES, this.#prng);
70+
}
71+
72+
clearData(): void {
73+
this.#tilemap.clearTiles();
74+
}
75+
76+
generateMap(size = 50): void {
77+
for (let x = 0; x < size; x++) {
78+
for (let y = 0; y < size; y++) {
79+
this.#tilemap.setTile(x, y, this.#getTile());
80+
}
81+
}
82+
}
83+
84+
onInitialize() {
85+
// Re‑seed whenever the network‑synced seed value changes.
86+
this.values.get("seed")?.onChanged(() => {
87+
this.#setRng();
88+
if (this.game.isServer()) this.generateMap();
89+
});
90+
91+
this.#setRng();
92+
if (this.game.isServer()) this.generateMap();
93+
}
94+
95+
onFrame(): void {
96+
if (!this.game.isClient()) return;
97+
98+
const world = this.inputs.cursor.world;
99+
if (!world) return;
100+
101+
const left = this.inputs.getKey("MouseLeft");
102+
const right = this.inputs.getKey("MouseRight");
103+
if (!left && !right) return;
104+
105+
const tile = this.#tilemap.getTileAtPoint(world);
106+
if (!tile) return;
107+
108+
const id = left ? 33 /* hard‑coded brush */ : this.#getTile();
109+
this.#tilemap.setTile(tile.x, tile.y, id);
110+
}
111+
}
112+
```
113+
114+
---
115+
116+
## Core API
117+
118+
| Method | Purpose |
119+
|------------------------------------------|-------------------------------------------------------------|
120+
| `getTile(x, y)` | Returns `TileData` \| `undefined` for that coordinate. |
121+
| `getTilePaletteId(x, y)` | Returns palette index (`number`) or `undefined`. |
122+
| `setTile(x, y, paletteId?)` | Sets tile; pass `undefined` to erase. |
123+
| `getTileAtPoint(worldPos)` | Ray-casts the cursor; returns `{ x, y, …tile }` or `undefined`. |
124+
| `clearTiles()` | Removes every tile from the map. |
125+
| `tiles()` | Generator that yields `{ x, y, tile }` for each filled cell.|
126+
| `bounds` *(getter)* | `{ width, height, offset }` in tile units. |
127+
128+
### `TileData` union
129+
130+
```ts
131+
type TileData =
132+
| { type: "color"; color: string; alpha?: number }
133+
| { type: "texture"; texture: string }
134+
| { type: "spritesheet"; spritesheet: string; frame: number }
135+
| { type: "texture-slice"; texture: string; x: number; y: number };
136+
```
137+
138+
---
139+
140+
### FAQ
141+
142+
**How big can a tilemap be?**
143+
Technically unlimited, but performance is tuned for ≤ 10 000 drawn tiles.
144+
145+
**Can I swap atlases at runtime?**
146+
Yes — just assign a new path to **Atlas**; all visuals update automatically.
147+
148+
**How do I force pixel-perfect textures?**
149+
Set **Scale filter mode** to “nearest” on the Tilemap *or* on the Camera.

0 commit comments

Comments
 (0)