As a quick little project, I created a little dungeon generator! It works like a charm, and I’ll probably use it in future projects.
Here’s the code-- feel free to use it or give any feedback
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DungeonGenerationManager : MonoBehaviour
{
public GameObject room_prefab;
public int roomSize;
#region GENERATION SETTINGS
public int maxRooms;
public int maxDirectionRepeat = 1;
#endregion
public int roomNumber;
bool deadEnd = false;
List<Vector3Int> occupiedCoordinates;
List<GameObject> spawnedRooms;
private static readonly List<Vector3Int> roomDirections = new List<Vector3Int>()
{
Vector3Int.forward,
Vector3Int.back,
Vector3Int.right,
Vector3Int.left,
//Vector3Int.up,
// Vector3Int.down
};
private void Start()
{
InitialIteration();
SecondIteration();
}
void InitialIteration() //Create rooms
{
occupiedCoordinates = new List<Vector3Int>();
spawnedRooms = new List<GameObject>();
Vector3Int currentPosition = Vector3Int.zero;
while(roomNumber < maxRooms)
{
deadEnd = false;
List<Vector3Int> possibleSpawnDirections = new List<Vector3Int>();
for(int i = 0; i < roomDirections.Count; i++)
{
Vector3Int possibleDirection = roomDirections[i];
if(!occupiedCoordinates.Contains(currentPosition + possibleDirection * roomSize))
{
possibleSpawnDirections.Add(possibleDirection);
}
}
if(possibleSpawnDirections.Count > 0 && !deadEnd)
{
Vector3Int chosenDirection = possibleSpawnDirections[Random.Range(0, possibleSpawnDirections.Count)];
int repeatTime = Random.Range(1, maxDirectionRepeat);
for(int j = 0; j < repeatTime; j++) //keep generating in this direction k times
{
Vector3Int possiblePosition = currentPosition + (chosenDirection * roomSize);
if(roomNumber < maxRooms) //the for-loop has to finish even if the conditions of the while-loop are met. If roomNumber is greater than maxRooms, but the for-loop isn't done,
//then more rooms will be spanwed regardless that we are above are room limit. This is why this if statement is here. Everything in the for-loop will only be run if the conditions are met.
{
if (!occupiedCoordinates.Contains(possiblePosition))
{
currentPosition = currentPosition + (chosenDirection * roomSize);
occupiedCoordinates.Add(currentPosition);
roomNumber++;
GameObject instantiatedRoom = Instantiate(room_prefab, currentPosition, Quaternion.identity);
instantiatedRoom.name = "Room_" + roomNumber;
spawnedRooms.Add(instantiatedRoom);
}
else
{
print("no more available spots!");
currentPosition = occupiedCoordinates[Random.Range(0, occupiedCoordinates.Count)];
deadEnd = true;
}
}
}
}
else //if there are no more available positions left, choose a random occupied position and branch from there!
{
print("no more available spots!");
currentPosition = occupiedCoordinates[Random.Range(0, occupiedCoordinates.Count)];
}
}
}
//make the room object have child 0 be the north wall, child 1 be the south wall, child 2 be the east wall, and child 3 be the west wall, to match the indices of the room directions list (i.e Vector3Int.forward is list item 0, and the north wall is child 0)
void SecondIteration()
{
foreach(GameObject room in spawnedRooms)
{
Vector3Int roomPos = new Vector3Int((int)room.transform.position.x, (int)room.transform.position.y, (int)room.transform.position.z);
for (int i = 0; i < roomDirections.Count; i++)
{
Vector3Int possibleDirection = roomDirections[i];
if (occupiedCoordinates.Contains(roomPos + possibleDirection * roomSize))
{
room.transform.GetChild(i).gameObject.SetActive(false); //remember the floor is the first child
}
}
}
}
}