Your First Map
Build a small dungeon with connected rooms, a locked door, and pathfinding in about 5 minutes.
By the end of this guide, you'll have a 4-room dungeon where players need a key to access the treasure room, and you'll know how to check if paths exist between locations.
What We're Building
A simple dungeon layout:
[Treasure Room]
|
(locked door)
|
[Guard Room] --EAST-- [Great Hall] --NORTH-- [Armory]
The treasure room requires a brass-key to enter.
Step 1: Create the Graph
Start by importing the library and creating an empty graph.
import { createSpatialGraph, Direction } from '@motioneffector/spatial'
const graph = createSpatialGraph()
The createSpatialGraph() function returns a new graph instance. All your rooms and connections will live here.
Step 2: Add Rooms
Create nodes for each location. Nodes need a unique ID, and you can attach metadata like names and descriptions.
graph.createNode('guard-room', {
name: 'Guard Room',
description: 'A small room with weapon racks on the walls.'
})
graph.createNode('great-hall', {
name: 'Great Hall',
description: 'A vast chamber with pillars reaching to the ceiling.'
})
graph.createNode('armory', {
name: 'Armory',
description: 'Shields and swords line every surface.'
})
graph.createNode('treasure-room', {
name: 'Treasure Room',
description: 'Gold coins glitter in the torchlight.'
})
Each call to createNode() registers a new location in the graph.
Step 3: Connect Rooms
Link rooms together using directions. Connections are bidirectional by default—connecting A to B also connects B to A in the opposite direction.
// Guard Room <-> Great Hall (via EAST/WEST)
graph.connect('guard-room', Direction.EAST, 'great-hall')
// Great Hall <-> Armory (via NORTH/SOUTH)
graph.connect('great-hall', Direction.NORTH, 'armory')
// Great Hall <-> Treasure Room (via EAST/WEST)
graph.connect('great-hall', Direction.EAST, 'treasure-room')
After these calls, a player in the Guard Room can go EAST to reach the Great Hall, and going WEST from the Great Hall returns to the Guard Room.
Step 4: Add a Locked Door
Make the treasure room require a key. Gates attach to existing connections and control who can pass.
graph.setGate('great-hall', Direction.EAST, {
id: 'treasury-door',
locked: true,
keyId: 'brass-key',
description: 'A heavy iron door with a brass lock.'
})
Now the connection from Great Hall to Treasure Room is blocked unless the player has the brass-key.
Step 5: Test Traversal
Check if a player can move through the locked door, with and without the key.
// Without the key
const blocked = graph.canTraverse('great-hall', Direction.EAST, {
inventory: []
})
console.log(blocked.allowed) // false
console.log(blocked.reason) // 'locked'
// With the key
const allowed = graph.canTraverse('great-hall', Direction.EAST, {
inventory: ['brass-key']
})
console.log(allowed.allowed) // true
The canTraverse() method checks gates and returns whether movement is allowed, plus a reason if blocked.
Step 6: Find a Path
Use pathfinding to get the route from the guard room to the treasure room.
// Path with the key
const path = graph.findPath('guard-room', 'treasure-room', {
context: { inventory: ['brass-key'] }
})
console.log(path) // ['guard-room', 'great-hall', 'treasure-room']
// Path without the key
const noPath = graph.findPath('guard-room', 'treasure-room', {
context: { inventory: [] }
})
console.log(noPath) // null (no valid path exists)
Pathfinding respects locked gates—it won't route through doors the player can't open.
The Complete Code
Here's everything together:
import { createSpatialGraph, Direction } from '@motioneffector/spatial'
// Create the graph
const graph = createSpatialGraph()
// Add rooms
graph.createNode('guard-room', {
name: 'Guard Room',
description: 'A small room with weapon racks on the walls.'
})
graph.createNode('great-hall', {
name: 'Great Hall',
description: 'A vast chamber with pillars reaching to the ceiling.'
})
graph.createNode('armory', {
name: 'Armory',
description: 'Shields and swords line every surface.'
})
graph.createNode('treasure-room', {
name: 'Treasure Room',
description: 'Gold coins glitter in the torchlight.'
})
// Connect rooms
graph.connect('guard-room', Direction.EAST, 'great-hall')
graph.connect('great-hall', Direction.NORTH, 'armory')
graph.connect('great-hall', Direction.EAST, 'treasure-room')
// Add locked door
graph.setGate('great-hall', Direction.EAST, {
id: 'treasury-door',
locked: true,
keyId: 'brass-key',
description: 'A heavy iron door with a brass lock.'
})
// Test traversal
const canEnter = graph.canTraverse('great-hall', Direction.EAST, {
inventory: ['brass-key']
})
console.log('Can enter treasury:', canEnter.allowed) // true
// Find path
const path = graph.findPath('guard-room', 'treasure-room', {
context: { inventory: ['brass-key'] }
})
console.log('Path to treasure:', path) // ['guard-room', 'great-hall', 'treasure-room']
What's Next?
Now that you have the basics:
- Understand nodes and connections better — Learn about metadata, one-way connections, and node removal
- Add more gate types — Hidden passages, conditional gates, and custom blocked messages
- Explore pathfinding options — Weighted costs, distance calculation, and reachability
- Browse the API — Full reference when you need details