Skip to content

Instantly share code, notes, and snippets.

@Ratstail91
Last active November 21, 2020 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ratstail91/71a1ac59f82494467d7311568d07f726 to your computer and use it in GitHub Desktop.
Save Ratstail91/71a1ac59f82494467d7311568d07f726 to your computer and use it in GitHub Desktop.
Incomplete dungeon generator - only implements binary space partitioning right now.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DungeonGenerator : MonoBehaviour {
//parameters
public int dungeonWidth = 100;
public int dungeonHeight = 100;
public int terminationThreshold = 10;
//structures
enum Direction {
VERTICAL = 0, HORIZONTAL = 1
}
enum TileType {
WALL = 0, EMPTY, ROOM
}
//lifecycle methods
int frameIndex = 0;
TileType[,] dungeonTileData = null;
void FixedUpdate() {
if (++frameIndex % 30 == 0) {
dungeonTileData = GenerateTileData(dungeonWidth, dungeonHeight);
}
}
void OnDrawGizmos() {
if (dungeonTileData != null) {
for (int i = 0; i < dungeonWidth; i++) {
for (int j = 0; j < dungeonHeight; j++) {
switch(dungeonTileData[i, j]) {
case TileType.WALL:
Gizmos.color = Color.grey;
Gizmos.DrawCube(new Vector3(i - dungeonWidth / 2, j - dungeonHeight / 2, 0), new Vector3(1, 1, 1));
break;
case TileType.EMPTY:
Gizmos.color = Color.white;
Gizmos.DrawCube(new Vector3(i - dungeonWidth / 2, j - dungeonHeight / 2, 0), new Vector3(1, 1, 1));
break;
case TileType.ROOM:
Gizmos.color = Color.blue;
Gizmos.DrawCube(new Vector3(i - dungeonWidth / 2, j - dungeonHeight / 2, 0), new Vector3(1, 1, 1));
break;
}
}
}
}
}
//generation methods
TileType[,] GenerateTileData(int width, int height) {
TileType[,] tileData = new TileType[width, height];
//zero the array
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
tileData[i, j] = TileType.WALL;
}
}
PartitionSpace(0, 0, width, height, ref tileData);
return tileData;
}
Vector2Int PartitionSpace(int x1, int y1, int x2, int y2, ref TileType[,] tileData) {
//calc the width & height
int width = x2 - x1;
int height = y2 - y1;
//check the termination threshold
if (width <= terminationThreshold || height <= terminationThreshold) {
//draw the box
return GenerateRoom(x1, y1, x2, y2, ref tileData);
}
//get the partition direction
Direction direction;
if (width == height) {
direction = (Direction)Random.Range(0, 2);
} else {
direction = (Direction)(height < width ? Direction.VERTICAL : Direction.HORIZONTAL);
}
//split the partition in the middle third
switch (direction) {
case Direction.VERTICAL: {
int splitPos = Random.Range(width/3, (width/3) * 2);
Vector2Int a = PartitionSpace(x1, y1, x1 + splitPos, y2, ref tileData);
Vector2Int b = PartitionSpace(x1 + splitPos + 1, y1, x2, y2, ref tileData);
return GenerateCorridor(a, b, ref tileData);
}
case Direction.HORIZONTAL: {
int splitPos = Random.Range(height/3, (height/3) * 2);
Vector2Int a = PartitionSpace(x1, y1, x2, y1 + splitPos, ref tileData);
Vector2Int b = PartitionSpace(x1, y1 + splitPos + 1, x2, y2, ref tileData);
return GenerateCorridor(a, b, ref tileData);
}
}
throw new System.InvalidOperationException("Unreachable");
}
Vector2Int GenerateRoom(int x1, int y1, int x2, int y2, ref TileType[,] tileData) {
//calc the partition width & height
int width = x2 - x1;
int height = y2 - y1;
//TODO: prefab room chance
//room bounds
int xBegin = Random.Range(x1 + 1, x1 + width/2);
int xEnd = Random.Range(x1 + width/2 + 1, x2 - 1);
int yBegin = Random.Range(y1 + 1, y1 + height/2);
int yEnd = Random.Range(y1 + height/2 + 1, y2 - 1);
for (int i = xBegin; i <= xEnd; i++) {
for (int j = yBegin; j <= yEnd; j++) {
tileData[i, j] = TileType.ROOM;
}
}
return new Vector2Int(Random.Range(xBegin, xEnd + 1), Random.Range(yBegin, yEnd + 1));
}
Vector2Int GenerateCorridor(Vector2Int a, Vector2Int b, ref TileType[,] tileData) {
Vector2Int ret = new Vector2Int(99999,99999); //return value
if (a == ret || b == ret) {
return ret;
}
int width = (int)Mathf.Abs(a.x - b.x);
int height = (int)Mathf.Abs(a.y - b.y);
//initial direction from a
Direction direction;
if (width == height) {
direction = (Direction)Random.Range(0, 2);
} else {
direction = width > height ? Direction.HORIZONTAL : Direction.VERTICAL;
}
//draw from a to b, overwriting wall tiles only
switch(direction) {
case Direction.HORIZONTAL:
for (int i = (int)Mathf.Min(a.x, b.x); i <= (int)Mathf.Max(a.x, b.x); i++) {
if (tileData[i, a.y] == TileType.WALL) {
tileData[i, a.y] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, i, a.y, ref ret);
}
for (int j = (int)Mathf.Min(a.y, b.y); j <= (int)Mathf.Max(a.y, b.y); j++) {
if (tileData[b.x, j] == TileType.WALL) {
tileData[b.x, j] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, (int)Mathf.Max(a.x, b.x), j, ref ret);
}
break;
case Direction.VERTICAL:
for (int j = (int)Mathf.Min(a.y, b.y); j <= (int)Mathf.Max(a.y, b.y); j++) {
if (tileData[a.x, j] == TileType.WALL) {
tileData[a.x, j] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, a.x, j, ref ret);
}
for (int i = (int)Mathf.Min(a.x, b.x); i <= (int)Mathf.Max(a.x, b.x); i++) {
if (tileData[i, b.y] == TileType.WALL) {
tileData[i, b.y] = TileType.EMPTY;
}
CheckCorridorCenter(width, height, i, (int)Mathf.Max(a.y, b.y), ref ret);
}
break;
}
return ret;
}
//utility
void CheckCorridorCenter(int width, int height, int i, int j, ref Vector2Int center) {
if (center == new Vector2Int(99999,99999) && (width + height)/2 < i + j) {
center = new Vector2Int(i, j);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment