Locked Doors
A door you can walk through is a transition. A door you have to earn is a puzzle. Locked doors are one of the oldest tricks in game design — find the key, open the way forward. Let’s combine what you know about items and doors to build one.
The gold circle is the key. The red door on the right won’t let you through until you’ve picked it up — then it turns green and you can pass. Try walking into it without the key first.
The key as a special item
A key is just an item with a side effect: it sets a flag on your game state. Add hasKey to your game object alongside anything else you’re tracking:
const game = {
currentRoom: 1,
tileSize: 30,
hasKey: false
};
The key sits in the world as a positioned object, exactly like a collectible from the previous tutorial. Check whether the player is on its tile each frame:
const KEY_POS = { tileX: 5, tileY: 2, room: 1 };
let keyCollected = false;
function checkKeyPickup() {
if (keyCollected) return;
if (game.currentRoom !== KEY_POS.room) return;
const cx = Math.floor((player.x + player.width / 2) / game.tileSize);
const cy = Math.floor((player.y + player.height / 2) / game.tileSize);
if (cx === KEY_POS.tileX && cy === KEY_POS.tileY) {
keyCollected = true;
game.hasKey = true;
app.stage.removeChild(keySprite);
}
}
The locked door tile
In your map, use a new tile value for the locked door — say 4. How you handle it depends on two things: whether the player can walk into it, and whether it triggers a room transition.
Block movement when locked:
function isSolid(x, y) {
const col = Math.floor(x / game.tileSize);
const row = Math.floor(y / game.tileSize);
const t = rooms[game.currentRoom].map[row][col];
if (t === 4 && !game.hasKey) return true; // Locked door is a wall
return t === 1;
}
This is the whole trick. When hasKey is false, tile 4 behaves like a wall. When it’s true, the player can walk through it and the normal door-transition logic takes over.
Mark the door in your doors table:
const doors = {
2: { toRoom: 2, playerX: 1, playerY: 4, locked: false },
4: { toRoom: 2, playerX: 1, playerY: 4, locked: true } // Requires key
};
Transition logic with key check:
function checkDoors() {
const map = rooms[game.currentRoom].map;
const cx = Math.floor((player.x + player.width / 2) / game.tileSize);
const cy = Math.floor((player.y + player.height / 2) / game.tileSize);
const tileType = map[cy][cx];
const door = doors[tileType];
if (!door) return;
if (door.locked && !game.hasKey) return; // Blocked
// Consume the key on use
if (door.locked) game.hasKey = false;
game.currentRoom = door.toRoom;
player.x = door.playerX * game.tileSize;
player.y = door.playerY * game.tileSize;
buildMap();
}
The key is consumed (game.hasKey = false) when the door is used. That’s optional — some games let one key open many doors, others are single-use. It’s a design decision, not a technical one.
Visual feedback
A locked door that looks the same as an open one is confusing. The simplest approach is to render tile 4 differently depending on game state:
function renderTile(tileType, col, row) {
let color;
if (tileType === 1) {
color = 0x4a3728; // Wall
} else if (tileType === 2) {
color = 0x44bb44; // Open door (green)
} else if (tileType === 4) {
color = game.hasKey
? 0x44bb44 // Unlocked (green)
: 0xcc4422; // Locked (red)
} else {
color = 0x1a1a2e; // Floor
}
// ... draw tile
}
Call buildMap() after the player picks up the key so the door redraws immediately. That instant colour change gives the player a clear signal that something has changed.
Keeping items across rooms
Now that items and doors exist in the same world, you need to persist item state when the player moves between rooms. Add this to your changeMap function:
function changeMap(newRoom) {
// Snapshot which items are still uncollected before leaving
myItems[game.currentRoom] = [...activeItems.values()].map(
item => [item.type, item.tileX, item.tileY]
);
activeItems.forEach(item => app.stage.removeChild(item.sprite));
activeItems.clear();
game.currentRoom = newRoom;
buildMap();
}
This rebuilds myItems[currentRoom] from whatever is still in activeItems. Anything collected is gone from there, so it won’t reappear when the player returns.
Next up: Pushing Tiles — make tiles in the world movable.