Tutorial 5 · Maps & Tiles
This page explains maps and tiles completely — the floor everything else stands on. Come back to it whenever you wonder what a tile can do.
A Retro-64 room is a character grid: 8×8 tiles, 40×25 to a screen — the C64's native character resolution. Rows are strings, and a legend maps each character to behaviour and looks. You've been using this since Tutorial 1; here's all of it.
The eight things a tile can be
Every tile behaviour is a simple on/off switch (the code calls them flags), and one tile can switch on several at once:
export const SOLID = 1; // blocks movement from every side
export const PLATFORM = 2; // blocks only when landing from above
export const HAZARD = 4; // kills on contact
export const CONVEYOR = 8; // drags whatever stands on it (tile.dir ±1, tile.beltSpeed)
export const CRUMBLE = 16; // decays while stood on (tile.hp ticks), then gives way
export const LADDER = 32; // climbable while overlapping
export const ICE = 64; // solid but low-grip — you slide (tile.grip 0..1)
export const BOUNCE = 128; // solid but launches on landing (tile.bounceVel)
Mixing them is the point — that | symbol just means and also. A conveyor is SOLID | CONVEYOR: something you can stand on that also drags you. Ice is SOLID | ICE. Nothing stops you inventing HAZARD | CONVEYOR — a belt of blades — because the player only ever asks a tile "what are you switched on as?", never "what's your name?".
Anatomy of a legend entry
const LEGEND = {
'>': {
flags: SOLID | CONVEYOR, // what it DOES — read by player and entities
kind: 'conveyor', // how it DRAWS — picks the renderer case
dir: 1, beltSpeed: 0.5, // behaviour parameters (per-flag, see above)
base: C64.GREY, // body colour (palette index)
top: C64.LIGHT_GREY, // top-edge/detail colour
},
};
kind selects a hand-pixelled drawing routine: brick, platform, spike, lava (animated bubbles), ice, bounce, shroom (the trampoline mushroom), conveyor (the chevrons crawl in the belt's direction — the animation is the signage), crumble (cracks appear as it wears), ladder, gate — plus a shelf of pure-scenery kinds (torch, banner, grass, flower, bush, tombstone, pillar, window, fence, mushroom, skull, chain) that draw but never collide: give them flags: 0 and dress your world. An unknown kind falls back to a plain filled square, so a new tile is playable before it's pretty.
Building the map
const map = new TileMap({
rows, // string[] — every row the same length, or it throws
legend,
tileSize: 8, // the default; the editor uses 8 too
openSides: false, // see below
});
Useful properties and methods:
map.pixelWidth/map.pixelHeight— size in pixels (w × tileSize).map.tileAt(cx, cy)→ the legend entry (ornullfor air).map.flagsAt(cx, cy)→ just the bits,0for air. These two are the whole collision API — the player, every entity, and your own code all read the world through them.map.draw(video, camX, camY, tick)— draws only the tiles the camera can see;tickdrives the animated kinds.
Edges: openSides and what lies beyond
What's off the map? The rules are asymmetric, and deliberately so:
- Top and bottom are always open — sky above (you can jump out of frame), and the void below (falling out kills, via the player's
'void'death). - The sides depend on
openSides.false(default): off the sides is solid wall — right for a self-contained, single-room game.true: the sides are open air, so a World can catch you half-way out and flick to the next room. The World sets this itself; you only think about it for single rooms.
Crumble: the one tile with memory
Crumbling floor is the only stateful tile. Each tick something stands on it, the player controller calls:
map.weaken(cx, cy); // default amount 1; hp runs out → the cell becomes air
map.wearStageAt(cx, cy); // 0 fresh · 1 cracking · 2 about to go — drives the cracks
map.onCrumble = (cx, cy, tile) => { /* dust puff here */ };
The state lives on the TileMap instance — build a fresh TileMap (or re-enter a World room) and the floor regrows. That's the Manor's crumbling bridge: no reset code, just reconstruction.
What you've learned
A map is strings + a legend; a tile is a bitmask + a drawing kind + a few parameters. Everything that reads the world does it through flagsAt, which is why new tile types need no changes anywhere else.