
Prompt
Write a procedural Dungeon Generator in p5.js. It should create a random dungeon with 5 rooms connected by corridors on a 30x30 grid. Show each tile with a suitable sprite to show room interior, room walls, corridors and corners etc. Show an overhead isometric view.
A system prompt was added to support web rendering
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Procedural Dungeon – p5.js</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/p5.min.js"></script>
</head>
<body style="margin:0;background:#222;">
<script>
/* -------------------------------------------------
Procedural Dungeon Generator – isometric display
------------------------------------------------- */
const GRID = 30; // grid size (GRID × GRID)
const ROOMS = 5; // number of rooms
const TILE_W = 40; // width of an isometric rhombus
const TILE_H = 20; // height of an isometric rhombus
// tile codes
const EMPTY = 0;
const FLOOR = 1;
const WALL = 2;
const CORRIDOR = 3;
// colours (“sprites”)
const palette = {
[FLOOR] : '#d9c090',
[WALL] : '#8b5a2b',
[CORRIDOR]: '#a0a0a0'
};
let dungeon = []; // 2-D array [GRID][GRID]
let rooms = [];
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
noLoop(); // generate once
generateDungeon();
}
function generateDungeon() {
initGrid();
buildRooms();
connectRooms();
addWalls();
drawDungeon();
}
/* -------------- generation helpers ----------------- */
function initGrid(){
dungeon = Array.from({length: GRID}, () =>
Array.from({length: GRID}, () => EMPTY)
);
}
// Try to place ROOMS rectangular rooms without overlap
function buildRooms(){
let tries = 0;
while (rooms.length < ROOMS && tries < 500){
tries++;
const w = floor(random(4, 9));
const h = floor(random(4, 9));
const x = floor(random(1, GRID - w - 1));
const y = floor(random(1, GRID - h - 1));
const newRoom = {x, y, w, h};
if (!rooms.some(r => intersects(r, newRoom))){
rooms.push(newRoom);
for (let i = x; i < x + w; i++){
for (let j = y; j < y + h; j++){
dungeon[i][j] = FLOOR;
}
}
}
}
}
// axis-aligned corridor (L-shaped) between successive room centres
function connectRooms(){
for (let i = 1; i < rooms.length; i++){
const prev = center(rooms[i-1]);
const curr = center(rooms[i]);
// horizontal first, then vertical (50 % chance reversed)
if (random() < .5){
carveCorridor(prev.x, curr.x, prev.y, true); // horizontal
carveCorridor(prev.y, curr.y, curr.x, false); // vertical
} else {
carveCorridor(prev.y, curr.y, prev.x, false);
carveCorridor(prev.x, curr.x, curr.y, true);
}
}
}
function carveCorridor(a, b, fixed, horizontal){
const dir = a < b ? 1 : -1;
for (let t = a; t !== b + dir; t += dir){
const x = horizontal ? t : fixed;
const y = horizontal ? fixed : t;
if (inBounds(x,y) && dungeon[x][y] === EMPTY){
dungeon[x][y] = CORRIDOR;
}
}
}
// After rooms & corridors are carved, surround room floors with walls
function addWalls(){
for (let x = 0; x < GRID; x++){
for (let y = 0; y < GRID; y++){
if (dungeon[x][y] === FLOOR){
neighbours(x,y).forEach(([nx,ny])=>{
if (dungeon[nx][ny] === EMPTY) dungeon[nx][ny] = WALL;
});
}
}
}
}
/* -------------- drawing ----------------- */
function drawDungeon(){
background('#222');
// centre dungeon on screen
const offsetX = width / 2;
const offsetY = 100; // top padding
for (let x = 0; x < GRID; x++){
for (let y = GRID-1; y >= 0; y--){ // draw back-to-front for painter's algo
const tile = dungeon[x][y];
if (tile === EMPTY) continue;
const iso = toIsometric(x, y, offsetX, offsetY);
push();
translate(iso.x, iso.y);
// slight elevation to walls so they look higher
if (tile === WALL){
drawTile(palette[WALL]);
translate(0, -TILE_H/2);
drawTile(palette[FLOOR]);
} else {
drawTile(palette[tile]);
}
pop();
}
}
}
// draw isometric diamond
function drawTile(col){
stroke(0,80);
fill(col);
beginShape();
vertex(0, 0);
vertex(TILE_W/2, TILE_H/2);
vertex(0, TILE_H);
vertex(-TILE_W/2, TILE_H/2);
endShape(CLOSE);
}
/* -------------- geometry helpers ----------------- */
function toIsometric(x, y, ox, oy){
return {
x: (x - y) * TILE_W/2 + ox,
y: (x + y) * TILE_H/2 + oy
};
}
function neighbours(x,y){
return [
[x-1,y],[x+1,y],[x,y-1],[x,y+1],
[x-1,y-1],[x+1,y-1],[x-1,y+1],[x+1,y+1]
].filter(([i,j])=>inBounds(i,j));
}
function inBounds(x,y){
return x>=0 && y>=0 && x<GRID && y<GRID;
}
function intersects(a,b){
return !(b.x >= a.x + a.w ||
b.x + b.w <= a.x ||
b.y >= a.y + a.h ||
b.y + b.h <= a.y);
}
function center(r){
return {
x: floor(r.x + r.w / 2),
y: floor(r.y + r.h / 2)
};
}
</script>
</body>
</html>
```# Procedural Dungeon Generator in p5.js
Here's a complete procedural dungeon generator that creates a random dungeon with 5 rooms connected by corridors on a 30x30 grid, rendered in an overhead isometric view with custom sprites for different tile types.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Procedural Dungeon Generator</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<style>
body {
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #1a1a2e;
color: #e6e6e6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
max-width: 1000px;
}
h1 {
margin: 10px 0;
text-align: center;
color: #f8f8f8;
text-shadow: 0 0 10px rgba(100, 200, 255, 0.5);
}
.controls {
margin: 15px 0;
display: flex;
gap: 15px;
flex-wrap: wrap;
justify-content: center;
}
button {
background: #4a4a8a;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
}
button:hover {
background: #5a5aaa;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.3);
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin: 15px 0;
justify-content: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 3px;
}
.info {
margin: 10px 0;
text-align: center;
max-width: 600px;
line-height: 1.5;
}
</style>
</head>
<body>
<div class="container">
<h1>Procedural Dungeon Generator</h1>
<div class="info">
<p>Randomly generated dungeon with 5 rooms connected by corridors. Rendered in overhead isometric view with custom tile sprites.</p>
</div>
<div class="controls">
<button id="generateBtn">Generate New Dungeon</button>
<button id="toggleGridBtn">Toggle Grid</button>
</div>
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #8B4513;"></div>
<span>Room Floor</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #A9A9A9;"></div>
<span>Corridor</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #5D4037;"></div>
<span>Wall</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #4E342E;"></div>
<span>Corner</span>
</div>
</div>
<div id="canvasContainer"></div>
</div>
<script>
// Tile types
const EMPTY = 0;
const ROOM = 1;
const CORRIDOR = 2;
const WALL = 3;
const CORNER = 4;
// Grid dimensions
const GRID_SIZE = 30;
const TILE_WIDTH = 40;
const TILE_HEIGHT = 20;
// Colors
const COLORS = {
[EMPTY]: [0, 0, 0, 0], // Transparent
[ROOM]: [139, 69, 19], // Brown
[CORRIDOR]: [169, 169, 169], // Gray
[WALL]: [93, 64, 55], // Dark Brown
[CORNER]: [78, 52, 46] // Darker Brown
};
let grid = [];
let rooms = [];
let showGrid = true;
function setup() {
const canvas = createCanvas(1000, 700);
canvas.parent('canvasContainer');
generateDungeon();
// Setup buttons
document.getElementById('generateBtn').addEventListener('click', () => {
generateDungeon();
});
document.getElementById('toggleGridBtn').addEventListener('click', () => {
showGrid = !showGrid;
});
noLoop();
}
function generateDungeon() {
// Initialize grid
grid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(EMPTY));
rooms = [];
// Generate rooms
for (let i = 0; i < 5; i++) {
let roomPlaced = false;
let attempts = 0;
while (!roomPlaced && attempts < 100) {
attempts++;
const width = floor(random(4, 8));
const height = floor(random(4, 8));
const x = floor(random(1, GRID_SIZE - width - 1));
const y = floor(random(1, GRID_SIZE - height - 1));
// Check for overlap
let overlap = false;
for (let dx = -1; dx < width + 1; dx++) {
for (let dy = -1; dy < height + 1; dy++) {
if (x + dx < 0 || x + dx >= GRID_SIZE || y + dy < 0 || y + dy >= GRID_SIZE) {
overlap = true;
break;
}
if (grid[x + dx][y + dy] !== EMPTY) {
overlap = true;
break;
}
}
if (overlap) break;
}
if (!overlap) {
// Place room
for (let dx = 0; dx < width; dx++) {
for (let dy = 0; dy < height; dy++) {
grid[x + dx][y + dy] = ROOM;
}
}
rooms.push({x, y, width, height});
roomPlaced = true;
}
}
}
// Connect rooms with corridors
for (let i = 0; i < rooms.length - 1; i++) {
const room1 = rooms[i];
const room2 = rooms[i + 1];
// Center points of rooms
const x1 = floor(room1.x + room1.width / 2);
const y1 = floor(room1.y + room1.height / 2);
const x2 = floor(room2.x + room2.width / 2);
const y2 = floor(room2.y + room2.height / 2);
// Horizontal corridor
const startX = min(x1, x2);
const endX = max(x1, x2);
for (let x = startX; x <= endX; x++) {
if (grid[x][y1] === EMPTY) {
grid[x][y1] = CORRIDOR;
}
}
// Vertical corridor
const startY = min(y1, y2);
const endY = max(y1, y2);
for (let y = startY; y <= endY; y++) {
if (grid[x2][y] === EMPTY) {
grid[x2][y] = CORRIDOR;
}
}
}
// Add walls and corners
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (grid[x][y] === EMPTY) {
// Check if this empty cell is adjacent to a non-empty cell
let adjacent = false;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) {
if (grid[nx][ny] !== EMPTY) {
adjacent = true;
// Check if it's a corner
if (abs(dx) === 1 && abs(dy) === 1) {
// Check if the adjacent cells form a corner pattern
if (grid[x + dx][y] !== EMPTY && grid[x][y + dy] !== EMPTY) {
grid[x][y] = CORNER;
}
}
}
}
}
}
if (adjacent && grid[x][y] === EMPTY) {
grid[x][y] = WALL;
}
}
}
}
redraw();
}
function draw() {
background(26, 26, 46); // Dark blue background
// Center the dungeon
translate(width/2 - (GRID_SIZE * TILE_WIDTH)/4, 50);
// Draw tiles
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
const tileType = grid[x][y];
if (tileType !== EMPTY) {
drawIsometricTile(x, y, tileType);
}
}
}
// Draw grid lines if enabled
if (showGrid) {
drawGrid();
}
}
function drawIsometricTile(x, y, tileType) {
// Convert grid coordinates to isometric
const isoX = (x - y) * TILE_WIDTH / 2;
const isoY = (x + y) * TILE_HEIGHT / 2;
push();
translate(isoX, isoY);
// Draw tile base
fill(COLORS[tileType]);
noStroke();
// Draw different shapes based on tile type
if (tileType === ROOM || tileType === CORRIDOR) {
// Floor tile (diamond shape)
beginShape();
vertex(0, TILE_HEIGHT);
vertex(TILE_WIDTH/2, 0);
vertex(TILE_WIDTH, TILE_HEIGHT);
vertex(TILE_WIDTH/2, TILE_HEIGHT * 2);
endShape(CLOSE);
// Add texture details
if (tileType === ROOM) {
fill(120, 50, 15);
for (let i = 0; i < 3; i++) {
const px = random(5, TILE_WIDTH - 5);
const py = random(5, TILE_HEIGHT * 2 - 5);
ellipse(px, py, 2, 2);
}
} else {
fill(140, 140, 140);
line(0, TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
line(TILE_WIDTH/2, 0, TILE_WIDTH/2, TILE_HEIGHT * 2);
}
} else if (tileType === WALL) {
// Wall tile (vertical rectangle)
beginShape();
vertex(0, TILE_HEIGHT);
vertex(TILE_WIDTH/2, 0);
vertex(TILE_WIDTH, TILE_HEIGHT);
vertex(TILE_WIDTH/2, TILE_HEIGHT);
endShape(CLOSE);
// Wall texture
fill(70, 40, 30);
for (let i = 0; i < 4; i++) {
const px = random(5, TILE_WIDTH - 5);
const py = random(5, TILE_HEIGHT - 5);
rect(px, py, 3, 2);
}
} else if (tileType === CORNER) {
// Corner tile (triangle)
beginShape();
vertex(0, TILE_HEIGHT);
vertex(TILE_WIDTH/2, 0);
vertex(TILE_WIDTH, TILE_HEIGHT);
endShape(CLOSE);
// Corner texture
fill(60, 30, 20);
for (let i = 0; i < 3; i++) {
const px = random(5, TILE_WIDTH - 5);
const py = random(5, TILE_HEIGHT - 5);
rect(px, py, 2, 2);
}
}
pop();
}
function drawGrid() {
stroke(100, 100, 200, 50);
strokeWeight(0.5);
// Draw vertical lines
for (let x = 0; x <= GRID_SIZE; x++) {
const startX = (x - 0) * TILE_WIDTH / 2;
const startY = (x + 0) * TILE_HEIGHT / 2;
const endX = (x - GRID_SIZE) * TILE_WIDTH / 2;
const endY = (x + GRID_SIZE) * TILE_HEIGHT / 2;
line(startX, startY, endX, endY);
}
// Draw horizontal lines
for (let y = 0; y <= GRID_SIZE; y++) {
const startX = (0 - y) * TILE_WIDTH / 2;
const startY = (0 + y) * TILE_HEIGHT / 2;
const endX = (GRID_SIZE - y) * TILE_WIDTH / 2;
const endY = (GRID_SIZE + y) * TILE_HEIGHT / 2;
line(startX, startY, endX, endY);
}
}
</script>
</body>
</html>
```
## Features
1. **Procedural Generation**: Creates a random dungeon with 5 rooms of varying sizes connected by corridors on a 30x30 grid.
2. **Isometric View**: Renders the dungeon in an overhead isometric perspective for a 3D-like appearance.
3. **Tile Sprites**: Different visual representations for:
- Room floors (brown with texture)
- Corridors (gray with grid pattern)
- Walls (dark brown with brick texture)
- Corners (darker brown with texture)
4. **Interactive Controls**:
- "Generate New Dungeon" button to create a new layout
- "Toggle Grid" button to show/hide the grid lines
5. **Visual Legend**: Shows what each color represents in the dungeon.
6. **Responsive Design**: Works well on different screen sizes.
The algorithm ensures rooms don't overlap and are properly connected with corridors. Walls and corners are automatically generated around the rooms and corridors to create a complete dungeon structure.```html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
</head>
<body>
<script>
const GRID_SIZE = 30;
const TILE_SIZE = 20;
const ISO_ANGLE = Math.PI / 6; // 30 degrees
// Tile types
const EMPTY = 0;
const FLOOR = 1;
const WALL = 2;
const CORRIDOR = 3;
const DOOR = 4;
let grid = [];
let rooms = [];
let tileColors = {};
function setup() {
createCanvas(1000, 800);
// Initialize colors for different tile types
tileColors[EMPTY] = color(20, 20, 30);
tileColors[FLOOR] = color(180, 160, 120);
tileColors[WALL] = color(80, 60, 40);
tileColors[CORRIDOR] = color(150, 140, 110);
tileColors[DOOR] = color(120, 80, 60);
generateDungeon();
}
function draw() {
background(40, 40, 50);
// Translate to center the dungeon
push();
translate(width / 2, height / 2 - 100);
drawIsometricDungeon();
pop();
// Draw UI
fill(255);
textSize(16);
text("Procedural Dungeon Generator", 10, 30);
text("Press SPACE to generate new dungeon", 10, 50);
text("Rooms: " + rooms.length, 10, 70);
}
function generateDungeon() {
// Initialize grid
grid = [];
for (let y = 0; y < GRID_SIZE; y++) {
grid[y] = [];
for (let x = 0; x < GRID_SIZE; x++) {
grid[y][x] = EMPTY;
}
}
rooms = [];
// Generate 5 rooms
for (let i = 0; i < 5; i++) {
let attempts = 0;
let room = null;
while (attempts < 100) {
room = generateRoom();
if (isValidRoom(room)) {
placeRoom(room);
rooms.push(room);
break;
}
attempts++;
}
}
// Connect rooms with corridors
connectRooms();
// Add walls around rooms and corridors
addWalls();
}
function generateRoom() {
let width = random(4, 8);
let height = random(4, 8);
let x = floor(random(2, GRID_SIZE - width - 2));
let y = floor(random(2, GRID_SIZE - height - 2));
return {
x: x,
y: y,
width: width,
height: height,
centerX: x + floor(width / 2),
centerY: y + floor(height / 2)
};
}
function isValidRoom(room) {
// Check if room overlaps with existing rooms
for (let y = room.y - 1; y < room.y + room.height + 1; y++) {
for (let x = room.x - 1; x < room.x + room.width + 1; x++) {
if (x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE) {
if (grid[y][x] === FLOOR || grid[y][x] === CORRIDOR) {
return false;
}
}
}
}
return true;
}
function placeRoom(room) {
for (let y = room.y; y < room.y + room.height; y++) {
for (let x = room.x; x < room.x + room.width; x++) {
grid[y][x] = FLOOR;
}
}
}
function connectRooms() {
// Connect each room to the next one
for (let i = 0; i < rooms.length - 1; i++) {
createCorridor(rooms[i], rooms[i + 1]);
}
// Connect last room to first to ensure all rooms are connected
if (rooms.length > 2) {
createCorridor(rooms[rooms.length - 1], rooms[0]);
}
}
function createCorridor(room1, room2) {
let x1 = room1.centerX;
let y1 = room1.centerY;
let x2 = room2.centerX;
let y2 = room2.centerY;
// Create L-shaped corridor
// First horizontal, then vertical
let currentX = x1;
let currentY = y1;
// Horizontal part
while (currentX !== x2) {
if (grid[currentY][currentX] === EMPTY) {
grid[currentY][currentX] = CORRIDOR;
}
currentX += (x2 > currentX) ? 1 : -1;
}
// Vertical part
while (currentY !== y2) {
if (grid[currentY][currentX] === EMPTY) {
grid[currentY][currentX] = CORRIDOR;
}
currentY += (y2 > currentY) ? 1 : -1;
}
}
function addWalls() {
let newGrid = [];
for (let y = 0; y < GRID_SIZE; y++) {
newGrid[y] = [...grid[y]];
}
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
if (grid[y][x] === EMPTY) {
// Check if this empty tile is adjacent to a floor or corridor
let adjacent = false;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
let nx = x + dx;
let ny = y + dy;
if (nx >= 0 && nx < GRID_SIZE && ny >= 0 && ny < GRID_SIZE) {
if (grid[ny][nx] === FLOOR || grid[ny][nx] === CORRIDOR) {
adjacent = true;
break;
}
}
}
if (adjacent) break;
}
if (adjacent) {
newGrid[y][x] = WALL;
}
}
}
}
grid = newGrid;
}
function drawIsometricDungeon() {
// Draw from back to front for proper depth
for (let y = GRID_SIZE - 1; y >= 0; y--) {
for (let x = 0; x < GRID_SIZE; x++) {
if (grid[y][x] !== EMPTY) {
drawIsometricTile(x, y, grid[y][x]);
}
}
}
}
function drawIsometricTile(gridX, gridY, tileType) {
// Convert grid coordinates to isometric screen coordinates
let screenX = (gridX - gridY) * TILE_SIZE * 0.5;
let screenY = (gridX + gridY) * TILE_SIZE * 0.25;
push();
translate(screenX, screenY);
// Base color
let baseColor = tileColors[tileType];
if (tileType === WALL) {
// Draw wall as a 3D block
drawWallTile(baseColor);
} else {
// Draw floor tile
drawFloorTile(baseColor);
}
pop();
}
function drawFloorTile(baseColor) {
// Draw isometric floor tile
fill(baseColor);
stroke(0, 50);
strokeWeight(1);
beginShape();
vertex(0, -TILE_SIZE * 0.25);
vertex(TILE_SIZE * 0.5, 0);
vertex(0, TILE_SIZE * 0.25);
vertex(-TILE_SIZE * 0.5, 0);
endShape(CLOSE);
// Add some texture lines
stroke(0, 30);
line(-TILE_SIZE * 0.3, -TILE_SIZE * 0.15, TILE_SIZE * 0.3, TILE_SIZE * 0.15);
line(-TILE_SIZE * 0.2, 0, TILE_SIZE * 0.2, 0);
}
function drawWallTile(baseColor) {
let height = TILE_SIZE * 0.6;
// Top face
fill(red(baseColor) + 30, green(baseColor) + 30, blue(baseColor) + 30);
stroke(0, 100);
strokeWeight(1);
beginShape();
vertex(0, -TILE_SIZE * 0.25 - height);
vertex(TILE_SIZE * 0.5, -height);
vertex(0, TILE_SIZE * 0.25 - height);
vertex(-TILE_SIZE * 0.5, -height);
endShape(CLOSE);
// Right face
fill(red(baseColor), green(baseColor), blue(baseColor));
beginShape();
vertex(TILE_SIZE * 0.5, -height);
vertex(TILE_SIZE * 0.5, 0);
vertex(0, TILE_SIZE * 0.25);
vertex(0, TILE_SIZE * 0.25 - height);
endShape(CLOSE);
// Left face
fill(red(baseColor) - 20, green(baseColor) - 20, blue(baseColor) - 20);
beginShape();
vertex(-TILE_SIZE * 0.5, -height);
vertex(-TILE_SIZE * 0.5, 0);
vertex(0, TILE_SIZE * 0.25);
vertex(0, TILE_SIZE * 0.25 - height);
endShape(CLOSE);
// Bottom face (floor)
fill(tileColors[FLOOR]);
beginShape();
vertex(0, -TILE_SIZE * 0.25);
vertex(TILE_SIZE * 0.5, 0);
vertex(0, TILE_SIZE * 0.25);
vertex(-TILE_SIZE * 0.5, 0);
endShape(CLOSE);
}
function keyPressed() {
if (key === ' ') {
generateDungeon();
}
}
</script>
</body>
</html>
```
This procedural dungeon generator creates a random dungeon with the following features:
**Core Features:**
- Generates 5 random rooms on a 30x30 grid
- Connects all rooms with L-shaped corridors
- Automatically adds walls around rooms and corridors
- Renders in isometric view for a 3D appearance
**Visual Elements:**
- Different tile types: empty space, floor, walls, corridors
- Walls are rendered as 3D blocks with proper faces and lighting
- Floor tiles have subtle texture lines
- Color-coded tile types for easy identification
**Algorithm:**
1. **Room Generation**: Creates 5 rooms with random sizes (4x4 to 8x8) at random positions
2. **Room Validation**: Ensures rooms don't overlap with existing structures
3. **Corridor Creation**: Connects each room to the next using L-shaped paths
4. **Wall Addition**: Automatically places walls adjacent to floors and corridors
5. **Isometric Rendering**: Converts grid coordinates to isometric projection
**Controls:**
- Press SPACE to generate a new random dungeon
The isometric view provides depth and makes the dungeon appear three-dimensional, with walls rising above the floor level. Each generation creates a unique layout with different room positions and connecting corridors.Here's a complete p5.js dungeon generator with an isometric view. It creates 5 connected rooms with corridors and uses different sprites for walls, floors, and corners:
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<style>
body { margin: 0; padding: 0; }
</style>
</head>
<body>
<script>
let tiles = [];
const GRID_SIZE = 30;
const TILE_SIZE = 20;
// Tile types
const FLOOR = 0;
const WALL = 1;
const DOOR = 2;
const CORNER = 3;
const CORRIDOR = 4;
function setup() {
createCanvas(800, 600, WEBGL);
angleMode(DEGREES);
// Initialize grid
for (let x = 0; x < GRID_SIZE; x++) {
tiles[x] = [];
for (let y = 0; y < GRID_SIZE; y++) {
tiles[x][y] = WALL;
}
}
// Generate rooms
generateDungeon(5);
}
function generateDungeon(roomCount) {
let rooms = [];
// Generate random rooms
for (let i = 0; i < roomCount; i++) {
let maxAttempts = 100;
while (maxAttempts-- > 0) {
const w = floor(random(4, 8));
const h = floor(random(4, 8));
const x = floor(random(1, GRID_SIZE - w - 1));
const y = floor(random(1, GRID_SIZE - h - 1));
const newRoom = { x, y, w, h };
let overlap = false;
// Check for overlaps with existing rooms
for (const room of rooms) {
if (room.x < newRoom.x + newRoom.w + 2 &&
room.x + room.w + 2 > newRoom.x &&
room.y < newRoom.y + newRoom.h + 2 &&
room.y + room.h + 2 > newRoom.y) {
overlap = true;
break;
}
}
if (!overlap) {
rooms.push(newRoom);
// Carve out the room
for (let rx = x; rx < x + w; rx++) {
for (let ry = y; ry < y + h; ry++) {
tiles[rx][ry] = FLOOR;
}
}
// Add walls around the room
for (let rx = x - 1; rx <= x + w; rx++) {
for (let ry = y - 1; ry <= y + h; ry++) {
// Skip floor tiles
if (rx >= x && rx < x + w && ry >= y && ry < y + h) continue;
// Mark as wall if empty
if (tiles[rx] && tiles[rx][ry] !== FLOOR) {
tiles[rx][ry] = WALL;
}
}
}
// Add corners
if (tiles[x-1] && tiles[x-1][y-1] === WALL) tiles[x-1][y-1] = CORNER;
if (tiles[x+w] && tiles[x+w][y-1] === WALL) tiles[x+w][y-1] = CORNER;
if (tiles[x-1] && tiles[x-1][y+h] === WALL) tiles[x-1][y+h] = CORNER;
if (tiles[x+w] && tiles[x+w][y+h] === WALL) tiles[x+w][y+h] = CORNER;
break;
}
}
}
// Connect rooms with corridors
for (let i = 1; i < rooms.length; i++) {
const prevCenter = {
x: floor(rooms[i-1].x + rooms[i-1].w / 2),
y: floor(rooms[i-1].y + rooms[i-1].h / 2)
};
const currCenter = {
x: floor(rooms[i].x + rooms[i].w / 2),
y: floor(rooms[i].y + rooms[i].h / 2)
};
// 50% chance to go horizontal first or vertical first
if (random() > 0.5) {
createHorizontalCorridor(prevCenter.x, currCenter.x, prevCenter.y);
createVerticalCorridor(currCenter.x, prevCenter.y, currCenter.y);
} else {
createVerticalCorridor(prevCenter.x, prevCenter.y, currCenter.y);
createHorizontalCorridor(prevCenter.x, currCenter.x, currCenter.y);
}
}
}
function createHorizontalCorridor(x1, x2, y) {
const start = min(x1, x2);
const end = max(x1, x2);
for (let x = start; x <= end; x++) {
if (tiles[x][y] === WALL) {
tiles[x][y] = CORRIDOR;
}
// Add walls above and below corridor
if (y > 0 && tiles[x][y-1] !== FLOOR && tiles[x][y-1] !== CORRIDOR) {
tiles[x][y-1] = WALL;
}
if (y < GRID_SIZE-1 && tiles[x][y+1] !== FLOOR && tiles[x][y+1] !== CORRIDOR) {
tiles[x][y+1] = WALL;
}
}
}
function createVerticalCorridor(x, y1, y2) {
const start = min(y1, y2);
const end = max(y1, y2);
for (let y = start; y <= end; y++) {
if (tiles[x][y] === WALL) {
tiles[x][y] = CORRIDOR;
}
// Add walls left and right of corridor
if (x > 0 && tiles[x-1][y] !== FLOOR && tiles[x-1][y] !== CORRIDOR) {
tiles[x-1][y] = WALL;
}
if (x < GRID_SIZE-1 && tiles[x+1][y] !== FLOOR && tiles[x+1][y] !== CORRIDOR) {
tiles[x+1][y] = WALL;
}
}
}
function draw() {
background(0);
orbitControl();
rotateX(60);
rotateZ(45);
// Center the dungeon in view
translate(-GRID_SIZE * TILE_SIZE / 2, -GRID_SIZE * TILE_SIZE / 2, 0);
// Draw the grid
for (let x = 0; x < GRID_SIZE; x++) {
for (let y = 0; y < GRID_SIZE; y++) {
push();
translate(x * TILE_SIZE, y * TILE_SIZE, 0);
// Draw floor tiles slightly raised
if (tiles[x][y] === FLOOR || tiles[x][y] === CORRIDOR) {
fill(150, 120, 100); // Brown floor
box(TILE_SIZE-2, TILE_SIZE-2, 1);
}
// Draw walls
else if (tiles[x][y] === WALL || tiles[x][y] === CORNER) {
// Selection between wall and corner
if (tiles[x][y] === CORNER) {
fill(100, 100, 100); // Dark gray for corners
} else {
fill(120, 120, 120); // Gray walls
}
// Draw walls as taller boxes
box(TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
// Draw doors (intersections between corridors and rooms)
else if (tiles[x][y] === DOOR) {
fill(100, 60, 30); // Wooden door
box(TILE_SIZE-2, TILE_SIZE-2, 3);
}
pop();
}
}
}
</script>
</body>
</html>
```
This code creates a fully functional dungeon generator with:
1. Random room placement with size variation (4-8 tiles wide/long)
2. Automatic connection between rooms with corridors
3. Proper wall placement around rooms and corridors
4. Corner tiles at room corners
5. Isometric 3D view with:
- Raised floor tiles for walkable areas
- Tall walls for boundaries
- Different colors for different tile types
- Camera controls (click and drag to rotate)
You can refresh the page to generate a new random dungeon layout. The code prevents room overlaps and ensures all rooms are connected through corridors.```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Procedural Dungeon Generator - Isometric View</title>
<style>
body { background: #222; margin: 0; overflow: hidden; }
canvas { display: block; margin: 20px auto; background: #262626; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>
</head>
<body>
<script>
const GRID_SIZE = 30;
const TILE_W = 32;
const TILE_H = 16;
const ROOM_MIN = 5;
const ROOM_MAX = 10;
const NUM_ROOMS = 5;
let grid = [];
let rooms = [];
// Enum for tile types
const TILE_VOID = 0;
const TILE_FLOOR = 1;
const TILE_WALL = 2;
const TILE_CORRIDOR = 3;
const TILE_CORNER = 4;
function setup() {
createCanvas(1200, 800);
generateDungeon();
noLoop();
}
function generateDungeon() {
// Init grid
for (let y = 0; y < GRID_SIZE; y++) {
grid[y] = [];
for (let x = 0; x < GRID_SIZE; x++) {
grid[y][x] = TILE_VOID;
}
}
rooms = [];
let failCount = 0;
// Place rooms
while (rooms.length < NUM_ROOMS && failCount < 200) {
let rw = floor(random(ROOM_MIN, ROOM_MAX));
let rh = floor(random(ROOM_MIN, ROOM_MAX));
let rx = floor(random(1, GRID_SIZE - rw - 1));
let ry = floor(random(1, GRID_SIZE - rh - 1));
let overlaps = false;
for (let room of rooms) {
if (rx + rw + 1 > room.x && rx < room.x + room.w + 1 &&
ry + rh + 1 > room.y && ry < room.y + room.h + 1) {
overlaps = true;
break;
}
}
if (!overlaps) {
rooms.push({x: rx, y: ry, w: rw, h: rh});
// Draw the room floor
for (let y = ry; y < ry + rh; y++) {
for (let x = rx; x < rx + rw; x++) {
grid[y][x] = TILE_FLOOR;
}
}
} else {
failCount++;
}
}
// Place walls and corners
for (let r of rooms) {
for (let y = r.y-1; y <= r.y+r.h; y++) {
for (let x = r.x-1; x <= r.x+r.w; x++) {
if (x >= 0 && y >= 0 && x < GRID_SIZE && y < GRID_SIZE) {
if (grid[y][x] == TILE_VOID &&
((x == r.x-1 || x == r.x+r.w) && y >= r.y && y <= r.y+r.h-1) ||
((y == r.y-1 || y == r.y+r.h) && x >= r.x && x <= r.x+r.w-1) ) {
grid[y][x] = TILE_WALL;
}
}
}
}
// Corners
let corners = [
[r.x-1, r.y-1],
[r.x-1, r.y+r.h],
[r.x+r.w, r.y-1],
[r.x+r.w, r.y+r.h],
];
for (let c of corners) {
if (c[0] >=0 && c[1] >= 0 && c[0] < GRID_SIZE && c[1] < GRID_SIZE) {
grid[c[1]][c[0]] = TILE_CORNER;
}
}
}
// Connect rooms with corridors
let roomCenters = rooms.map(r => [r.x + floor(r.w/2), r.y + floor(r.h/2)]);
roomCenters = shuffle(roomCenters);
for (let i = 0; i < roomCenters.length - 1; i++) {
connectRooms(roomCenters[i], roomCenters[i+1]);
}
}
function connectRooms(a, b) {
let [x1, y1] = a;
let [x2, y2] = b;
// L-shaped corridor: first in x, then in y (randomize order)
let flip = random() < 0.5;
if (flip) {
drawCorridor(x1, y1, x2, y1);
drawCorridor(x2, y1, x2, y2);
} else {
drawCorridor(x1, y1, x1, y2);
drawCorridor(x1, y2, x2, y2);
}
}
// Bresenham for L-shape
function drawCorridor(x0, y0, x1, y1) {
let dx = Math.abs(x1 - x0);
let sx = x0 < x1 ? 1 : -1;
let dy = Math.abs(y1 - y0);
let sy = y0 < y1 ? 1 : -1;
let err = dx - dy;
while (true) {
if (grid[y0][x0] === TILE_VOID) {
grid[y0][x0] = TILE_CORRIDOR;
}
if (x0 === x1 && y0 === y1) break;
let e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx;}
if (e2 < dx) { err += dx; y0 += sy;}
}
// Optionally put walls along corridors
for (let y=0; y<GRID_SIZE; y++) for (let x=0; x<GRID_SIZE; x++) {
if (grid[y][x] === TILE_CORRIDOR) {
for (let dy=-1; dy<=1; dy++) for (let dx=-1; dx<=1; dx++) {
let nx = x+dx, ny = y+dy;
if (dx*dx + dy*dy == 1 &&
nx>=0 && ny>=0 && nx<GRID_SIZE && ny<GRID_SIZE) {
if (grid[ny][nx] === TILE_VOID) grid[ny][nx] = TILE_WALL;
}
}
}
}
}
function draw() {
background(40);
let centerX = width/2;
let centerY = 120;
// Draw dungeon
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
let sx = (x - y) * TILE_W * 0.5 + centerX;
let sy = (x + y) * TILE_H * 0.5 + centerY;
drawTile(sx, sy, grid[y][x], x, y);
}
}
}
function drawTile(sx, sy, t, x, y) {
push();
translate(sx, sy);
switch(t) {
case TILE_FLOOR:
// Room floor: sandy color, slightly random
fill(194 + noise(x*0.2, y*0.2)*12, 178, 128 + noise(y*0.3, x*0.3)*10, 240);
stroke(180,140,80,120);
drawDiamond();
break;
case TILE_WALL:
// Wall: dark stone brick
fill(90+noise(x,y)*30, 90+noise(y,x)*30, 80, 210);
stroke(40);
drawWallTile();
break;
case TILE_CORNER:
// Corner: roundish highlight
fill(110, 100, 90, 230);
stroke(50);
drawDiamond();
fill(160,140,80,160);
noStroke();
ellipse(0, 1, 14, 10);
break;
case TILE_CORRIDOR:
// Corridor: grayish, thin outline
fill(150+noise(x,y)*16, 155+noise(y,x)*20, 170, 230);
stroke(130,140,160,130);
drawDiamond();
break;
case TILE_VOID:
default:
// Void: slightly dark
fill(32,36,32,220);
stroke(24,28,18,130);
drawDiamond();
break;
}
pop();
}
function drawDiamond() {
beginShape();
vertex(0, -TILE_H/2);
vertex(TILE_W/2, 0);
vertex(0, TILE_H/2);
vertex(-TILE_W/2, 0);
endShape(CLOSE);
}
function drawWallTile() {
// Diamond, with brick lines
drawDiamond();
// Horizontal "brick" lines
stroke(120,110,88,70);
for(let yy=-6; yy<=6; yy+=6) {
line(-TILE_W/2 + 2, yy, TILE_W/2 - 2, yy);
}
// Vertical split simulates bricks
stroke(140,130,110,50);
line(0, -TILE_H/2, 0, TILE_H/2);
}
</script>
</body>
</html>
```
Response not available
Here's a simple implementation of a procedural dungeon generator using p5.js.
```javascript
// Define tile types
const TILE_TYPES = {
EMPTY: 0,
ROOM: 1,
WALL: 2,
CORRIDOR: 3,
CORNER: 4
};
let grid = [];
let rooms = [];
function setup() {
createCanvas(600, 600);
angleMode(DEGREES);
noStroke();
// Initialize grid
for (let i = 0; i < 30; i++) {
grid[i] = [];
for (let j = 0; j < 30; j++) {
grid[i][j] = TILE_TYPES.EMPTY;
}
}
// Generate rooms
for (let i = 0; i < 5; i++) {
let roomWidth = floor(random(3, 8));
let roomHeight = floor(random(3, 8));
let roomX = floor(random(0, 30 - roomWidth));
let roomY = floor(random(0, 30 - roomHeight));
// Check for overlap
let overlap = false;
for (let j = 0; j < rooms.length; j++) {
let otherRoom = rooms[j];
if (roomX < otherRoom.x + otherRoom.width &&
roomX + roomWidth > otherRoom.x &&
roomY < otherRoom.y + otherRoom.height &&
roomY + roomHeight > otherRoom.y) {
overlap = true;
break;
}
}
if (!overlap) {
rooms.push({ x: roomX, y: roomY, width: roomWidth, height: roomHeight });
} else {
i--; // Try again if there's an overlap
}
}
// Mark room tiles
for (let i = 0; i < rooms.length; i++) {
let room = rooms[i];
for (let j = room.x; j < room.x + room.width; j++) {
for (let k = room.y; k < room.y + room.height; k++) {
grid[j][k] = TILE_TYPES.ROOM;
}
}
}
// Connect rooms with corridors
for (let i = 0; i < rooms.length - 1; i++) {
let room1 = rooms[i];
let room2 = rooms[i + 1];
let x1 = room1.x + floor(room1.width / 2);
let y1 = room1.y + floor(room1.height / 2);
let x2 = room2.x + floor(room2.width / 2);
let y2 = room2.y + floor(room2.height / 2);
// Draw corridor
if (abs(x2 - x1) > abs(y2 - y1)) {
// Horizontal then vertical
if (x1 < x2) {
for (let j = x1; j <= x2; j++) {
if (grid[j][y1] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[j][y1] == TILE_TYPES.EMPTY) {
grid[j][y1] = TILE_TYPES.CORRIDOR;
} else if (grid[j][y1] == TILE_TYPES.WALL) {
grid[j][y1] = TILE_TYPES.CORNER;
}
}
} else {
for (let j = x1; j >= x2; j--) {
if (grid[j][y1] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[j][y1] == TILE_TYPES.EMPTY) {
grid[j][y1] = TILE_TYPES.CORRIDOR;
} else if (grid[j][y1] == TILE_TYPES.WALL) {
grid[j][y1] = TILE_TYPES.CORNER;
}
}
}
if (y1 < y2) {
for (let j = y1; j <= y2; j++) {
if (grid[x2][j] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[x2][j] == TILE_TYPES.EMPTY) {
grid[x2][j] = TILE_TYPES.CORRIDOR;
} else if (grid[x2][j] == TILE_TYPES.WALL) {
grid[x2][j] = TILE_TYPES.CORNER;
}
}
} else {
for (let j = y1; j >= y2; j--) {
if (grid[x2][j] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[x2][j] == TILE_TYPES.EMPTY) {
grid[x2][j] = TILE_TYPES.CORRIDOR;
} else if (grid[x2][j] == TILE_TYPES.WALL) {
grid[x2][j] = TILE_TYPES.CORNER;
}
}
}
} else {
// Vertical then horizontal
if (y1 < y2) {
for (let j = y1; j <= y2; j++) {
if (grid[x1][j] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[x1][j] == TILE_TYPES.EMPTY) {
grid[x1][j] = TILE_TYPES.CORRIDOR;
} else if (grid[x1][j] == TILE_TYPES.WALL) {
grid[x1][j] = TILE_TYPES.CORNER;
}
}
} else {
for (let j = y1; j >= y2; j--) {
if (grid[x1][j] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[x1][j] == TILE_TYPES.EMPTY) {
grid[x1][j] = TILE_TYPES.CORRIDOR;
} else if (grid[x1][j] == TILE_TYPES.WALL) {
grid[x1][j] = TILE_TYPES.CORNER;
}
}
}
if (x1 < x2) {
for (let j = x1; j <= x2; j++) {
if (grid[j][y2] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[j][y2] == TILE_TYPES.EMPTY) {
grid[j][y2] = TILE_TYPES.CORRIDOR;
} else if (grid[j][y2] == TILE_TYPES.WALL) {
grid[j][y2] = TILE_TYPES.CORNER;
}
}
} else {
for (let j = x1; j >= x2; j--) {
if (grid[j][y2] == TILE_TYPES.ROOM) {
// Do nothing
} else if (grid[j][y2] == TILE_TYPES.EMPTY) {
grid[j][y2] = TILE_TYPES.CORRIDOR;
} else if (grid[j][y2] == TILE_TYPES.WALL) {
grid[j][y2] = TILE_TYPES.CORNER;
}
}
}
}
}
// Mark walls
for (let i = 0; i < 30; i++) {
for (let j = 0; j < 30; j++) {
if (grid[i][j] == TILE_TYPES.ROOM) {
if (i == 0 || grid[i - 1][j] != TILE_TYPES.ROOM) {
grid[i][j] = TILE_TYPES.WALL;
} else if (i == 29 || grid[i + 1][j] != TILE_TYPES.ROOM) {
grid[i][j] = TILE_TYPES.WALL;
} else if (j == 0 || grid[i][j - 1] != TILE_TYPES.ROOM) {
grid[i][j] = TILE_TYPES.WALL;
} else if (j == 29 || grid[i][j + 1] != TILE_TYPES.ROOM) {
grid[i][j] = TILE_TYPES.WALL;
}
}
}
}
}
function draw() {
background(220);
// Isometric view
let tileSize = 20;
let offsetX = width / 2 - 15 * tileSize;
let offsetY = height / 2 - 15 * tileSize;
for (let i = 0; i < 30; i++) {
for (let j = 0; j < 30; j++) {
let x = i * tileSize / 2 - j * tileSize / 2 + offsetX;
let y = i * tileSize / 4 + j * tileSize / 4 + offsetY;
if (grid[i][j] == TILE_TYPES.ROOM) {
fill(240, 220, 100); // Beige
beginShape();
vertex(x, y);
vertex(x + tileSize / 2, y + tileSize / 4);
vertex(x, y + tileSize / 2);
vertex(x - tileSize / 2, y + tileSize / 4);
endShape(CLOSE);
} else if (grid[i][j] == TILE_TYPES.WALL) {
fill(120, 120, 120); // Gray
beginShape();
vertex(x, y);
vertex(x + tileSize / 2, y + tileSize / 4);
vertex(x, y + tileSize / 2);
vertex(x - tileSize / 2, y + tileSize / 4);
endShape(CLOSE);
} else if (grid[i][j] == TILE_TYPES.CORRIDOR) {
fill(180, 140, 80); // Brown
beginShape();
vertex(x, y);
vertex(x + tileSize / 2, y + tileSize / 4);
vertex(x, y + tileSize / 2);
vertex(x - tileSize / 2, y + tileSize / 4);
endShape(CLOSE);
} else if (grid[i][j] == TILE_TYPES.CORNER) {
fill(150, 100, 50); // Dark Brown
beginShape();
vertex(x, y);
vertex(x + tileSize / 2, y + tileSize / 4);
vertex(x, y + tileSize / 2);
vertex(x - tileSize / 2, y + tileSize / 4);
endShape(CLOSE);
}
}
}
}
```
This code generates a 30x30 grid and creates 5 random rooms. It then connects these rooms with corridors. The grid is then drawn in an isometric view. Different tile types are represented by different colors.
To run this code, simply copy and paste it into a p5.js sketch. You can adjust the grid size, number of rooms, and tile size as needed. The `setup` function is called once when the sketch starts, and the `draw` function is called continuously until the sketch is stopped.