Skip to content

Instantly share code, notes, and snippets.

@pppoe252110
Created January 14, 2024 02:13
Show Gist options
  • Save pppoe252110/066f4b3de6424ea04d9527bc37593130 to your computer and use it in GitHub Desktop.
Save pppoe252110/066f4b3de6424ea04d9527bc37593130 to your computer and use it in GitHub Desktop.
Dungeon Rooms Proceed Generator
using System.Collections.Generic;
using UnityEngine;
public class DungeonCell : MonoBehaviour
{
public List<int> connections = new List<int>();
public int roomId = -1;
public Transform leftWall;
public Transform rightWall;
public Transform forwardWall;
public Transform backWall;
public Transform centerFloor;
public Transform centerCeiling;
public bool leftSet;
public bool rightSet;
public bool forwardSet;
public bool backSet;
public void SetLeftWall(GameObject obj)
{
leftSet = true;
obj.transform.parent = leftWall;
}
public void SetRightWall(GameObject obj)
{
rightSet = true;
obj.transform.parent = rightWall;
}
public void SetForwardWall(GameObject obj)
{
forwardSet = true;
obj.transform.parent = forwardWall;
}
public void SetBackWall(GameObject obj)
{
backSet = true;
obj.transform.parent = backWall;
}
public void SetCenterFloor(GameObject obj)
{
obj.transform.parent = centerFloor;
}
public void SetCenterCeiling(GameObject obj)
{
obj.transform.parent = centerCeiling;
}
//private void OnDrawGizmos()
//{
// Random.InitState(roomId);
// Gizmos.color = Random.ColorHSV();
// Gizmos.DrawCube(
// new Vector3(centerWall.transform.position.x, centerWall.transform.position.y, centerWall.transform.position.z),
// new Vector3(2.5f, 0, 2.5f));
//}
}
using Sirenix.OdinInspector;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
public class DungeonGenerator : MonoBehaviour
{
[Title("Prefabs", "Prefabs and room parts", TitleAlignments.Centered)]
[SerializeField] private DungeonCell _cellPrefab;
[SerializeField] private DungeonRoomData[] _roomsData;
[TitleGroup("Generation", "Seed and rooms properies", TitleAlignments.Centered)]
[Title("Randomization")]
[SerializeField] private int seed = 1;
[Title("Room Properties")]
[SerializeField] private Vector3 _roomSize = new Vector3(5, 5, 5);
[SerializeField] private Vector3Int _roomBoundsMin = new Vector3Int(2, 0, 2);
[SerializeField] private Vector3Int _roomBoundsMax = new Vector3Int(5, 0, 5);
[SerializeField] private Dictionary<Vector3Int, DungeonCell> _cells;
[Min(1)]
[SerializeField] private int _width = 10;
[Min(1)]
[SerializeField] private int _length = 10;
[Title("Room Joining", "Joining rooms", TitleAlignments.Centered)]
[SerializeField] private bool _joinSmallRooms = false;
[Min(1)]
[SerializeField] private int _maxRoomCellsJoin = 4;
[Sirenix.OdinInspector.DisplayAsString()]
private string lastMS;
private void Start()
{
Clear();
GenerateWithNewSeed();
}
[Button]
private void JustGenerate()
{
Generate();
}
[Button]
private void GenerateWithNewSeed()
{
seed++;
Generate();
}
[Button]
private void Clear()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
EditorApplication.delayCall += delegate
{
for (int i = transform.childCount - 1; i >= 0; i--)
{
DestroyImmediate(transform.GetChild(i).gameObject);
}
};
_cells = new Dictionary<Vector3Int, DungeonCell>();
return;
}
#endif
for (int i = transform.childCount - 1; i >= 0; i--)
{
Destroy(transform.GetChild(i).gameObject);
}
_cells = new Dictionary<Vector3Int, DungeonCell>();
}
private void Generate()
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
int id = 0;
int itterations = 0;
if (_cells?.Count > 0)
{
var cells = _cells.Values.ToArray();
for (int i = 0; i < cells.Length; i++)
{
if (cells[i] != null)
DestroyImmediate(cells[i]?.gameObject);
}
}
_cells = new Dictionary<Vector3Int, DungeonCell>();
Random.InitState(seed);
for (int x = 0; x < _width; x++)
{
for (int z = 0; z < _length; z++)
{
var pos = new Vector3(x * _roomSize.x + _roomSize.x / 2f, _roomSize.y / 2f, z * _roomSize.z + _roomSize.z / 2f);
var cell = Instantiate(_cellPrefab, pos, Quaternion.identity);
cell.transform.parent = transform;
_cells.Add(new Vector3Int(x, 0, z), cell);
}
}
List<DungeonCell> freeCells = _cells.Values.ToList();
while (freeCells.Count > 0)
{
if (itterations >= _width * _length)
{
//Debug.Log("PIZDEC");
break;
}
var rCell = freeCells[Random.Range(0, freeCells.Count)];
var cellPos = _cells.First(s => s.Value == rCell).Key;
var maxWidth = Random.Range(_roomBoundsMin.x, _roomBoundsMax.x + 1);
var maxLength = Random.Range(_roomBoundsMin.z, _roomBoundsMax.z + 1);
Vector3Int max = GetRoomMaxBound(cellPos, maxWidth, maxLength);
for (int x = 0; x < max.x; x++)
{
for (int z = 0; z < max.z; z++)
{
var targetCell = _cells.GetValueOrDefault(new Vector3Int(cellPos.x + x, cellPos.y, cellPos.z + z));
targetCell.roomId = id;
freeCells.Remove(targetCell);
}
}
itterations++;
id++;
}
if (_joinSmallRooms)
{
JoinRooms();
}
GenerateDoors();
GenerateWalls();
GenerateFloor();
GenerateCeiling();
stopwatch.Stop();
lastMS = stopwatch.ElapsedMilliseconds.ToString();
Debug.Log(stopwatch.ElapsedMilliseconds + "ms");
}
public void JoinRooms()
{
var rooms = _cells.GroupBy(s => s.Value.roomId).Where(s => s.Count() <= _maxRoomCellsJoin).ToList();
for (int i = 0; i < rooms.Count; i++)
{
foreach (var room in rooms[i])
{
if (_cells.TryGetValue(new Vector3Int(room.Key.x + 1, room.Key.y, room.Key.z), out var rightCell))
{
if (rooms.Any(s => s.Any(a => a.Value == rightCell && a.Value.roomId != room.Value.roomId)))
{
var cellsToJoin = _cells.Where(s => s.Value.roomId == rightCell.roomId);
foreach (var item in cellsToJoin)
{
item.Value.roomId = room.Value.roomId;
}
}
}
if (_cells.TryGetValue(new Vector3Int(room.Key.x, room.Key.y, room.Key.z + 1), out var forwardCell))
{
if (rooms.Any(s => s.Any(a => a.Value == forwardCell && a.Value.roomId != room.Value.roomId)))
{
var cellsToJoin = _cells.Where(s => s.Value.roomId == forwardCell.roomId);
foreach (var item in cellsToJoin)
{
item.Value.roomId = room.Value.roomId;
}
}
}
}
}
}
public Vector3Int GetRoomMaxBound(Vector3Int cellPos, int maxWidth, int maxLength)
{
int resultWidth = maxWidth;
int resultLength = maxLength;
List<List<Vector3Int>> results = new List<List<Vector3Int>>();
for (int x = 0; x < resultWidth; x++)
{
for (int z = 0; z < resultLength; z++)
{
var targetX = x;
var targetZ = z;
if (x + cellPos.x >= _width)
{
targetX = _width - cellPos.x - 1;
}
if (z + cellPos.z >= _length)
{
targetZ = _length - cellPos.z - 1;
}
if (GetFreeCells(targetX, targetZ, cellPos, out var cells))
{
results.Add(cells);
}
}
}
var max = results.OrderByDescending(x => x.Count).First().OrderByDescending(s => s.x + s.z).First();
return max;
}
public bool GetFreeCells(int x, int z, Vector3Int cellPos, out List<Vector3Int> cells)
{
List<Vector3Int> checkCells = new List<Vector3Int>();
for (int x1 = 0; x1 <= x; x1++)
{
for (int z1 = 0; z1 <= z; z1++)
{
var freeCell = _cells.GetValueOrDefault(new Vector3Int(cellPos.x + x1, 0, cellPos.z + z1));
if (freeCell.roomId != -1)
{
cells = null;
return false;
}
else
{
checkCells.Add(new Vector3Int(x1 + 1, 0, z1 + 1));
}
}
}
cells = checkCells;
return true;
}
public void GenerateFloor()
{
for (int x = 0; x < _width; x++)
{
for (int z = 0; z < _length; z++)
{
if (_cells.TryGetValue(new Vector3Int(x, 0, z), out var cell))
{
var floor = Instantiate(_roomsData[0].floorPrefab, cell.centerFloor.position, cell.centerFloor.rotation);
cell.SetCenterFloor(floor);
}
}
}
}
public void GenerateCeiling()
{
for (int x = 0; x < _width; x++)
{
for (int z = 0; z < _length; z++)
{
if (_cells.TryGetValue(new Vector3Int(x, 0, z), out var cell))
{
var ceiling = Instantiate(_roomsData[0].ceilingPrefab, cell.centerCeiling.position, cell.centerCeiling.rotation);
cell.SetCenterFloor(ceiling);
}
}
}
}
public void GenerateDoors()
{
var rooms = _cells.GroupBy(s => s.Value.roomId).ToList();
for (int i = 0; i < rooms.Count; i++)
{
foreach (var room in rooms[i])
{
if (_cells.TryGetValue(new Vector3Int(room.Key.x + 1, room.Key.y, room.Key.z), out var rightCell))
{
if (room.Value.roomId != rightCell.roomId &&
!_cells.Where(s => s.Value.roomId == room.Value.roomId).Any(s => s.Value.connections.Contains(room.Value.roomId)))
{
var wall = Instantiate(_roomsData[0].doorPrefab, room.Value.rightWall.position, room.Value.rightWall.rotation);
room.Value.SetRightWall(wall);
room.Value.connections.Add(room.Value.roomId);
}
}
if (_cells.TryGetValue(new Vector3Int(room.Key.x, room.Key.y, room.Key.z + 1), out var forwardCell))
{
if (room.Value.roomId != forwardCell.roomId &&
!_cells.Where(s => s.Value.roomId == room.Value.roomId).Any(s => s.Value.connections.Contains(room.Value.roomId)))
{
var wall = Instantiate(_roomsData[0].doorPrefab, room.Value.forwardWall.position, room.Value.forwardWall.rotation);
room.Value.SetForwardWall(wall);
room.Value.connections.Add(room.Value.roomId);
}
}
}
}
}
public void GenerateWalls()
{
for (int x = 0; x < _width; x++)
{
for (int z = 0; z < _length; z++)
{
var cell = _cells.GetValueOrDefault(new Vector3Int(x, 0, z));
if (_cells.TryGetValue(new Vector3Int(x, 0, z + 1), out var cellF) && cell.roomId != cellF.roomId)
{
if (!cell.forwardSet)
{
var wall = Instantiate(_roomsData[0].wallPrefab, cell.forwardWall.position, cell.forwardWall.rotation);
cell.SetForwardWall(wall);
}
}
if (_cells.TryGetValue(new Vector3Int(x + 1, 0, z), out var cellR) && cell.roomId != cellR.roomId)
{
if (!cell.rightSet)
{
var wall = Instantiate(_roomsData[0].wallPrefab, cell.rightWall.position, cell.rightWall.rotation);
cell.SetRightWall(wall);
}
}
if (!_cells.GetValueOrDefault(new Vector3Int(x, 0, z + 1)))
{
if (!cell.forwardSet)
{
var wall = Instantiate(_roomsData[0].wallPrefab, cell.forwardWall.position, cell.forwardWall.rotation);
cell.SetForwardWall(wall);
}
}
if (!_cells.GetValueOrDefault(new Vector3Int(x + 1, 0, z)))
{
if (!cell.rightSet)
{
var wall = Instantiate(_roomsData[0].wallPrefab, cell.rightWall.position, cell.rightWall.rotation);
cell.SetRightWall(wall);
}
}
if (!_cells.GetValueOrDefault(new Vector3Int(x, 0, z - 1)))
{
if (!cell.backSet)
{
var wall = Instantiate(_roomsData[0].wallPrefab, cell.backWall.position, cell.backWall.rotation);
cell.SetBackWall(wall);
}
}
if (!_cells.GetValueOrDefault(new Vector3Int(x - 1, 0, z)))
{
if (!cell.leftSet)
{
var wall = Instantiate(_roomsData[0].wallPrefab, cell.leftWall.position, cell.leftWall.rotation);
cell.SetLeftWall(wall);
}
}
}
}
}
//public Vector3Int GetRoomMaxBound(Vector3Int cellPos, int maxWidth, int maxLength)
//{
// int resultWidth = maxWidth;
// int resultLength = maxLength;
// Vector3Int lastCheckPos = Vector3Int.zero;
// for (int x = 0; x < resultWidth; x++)
// {
// for (int z = 0; z < resultLength; z++)
// {
// if (x + cellPos.x >= _width)
// {
// resultWidth = x;
// break;
// }
// if (z + cellPos.z >= _length)
// {
// resultLength = z;
// break;
// }
// var freeCell = _cells.GetValueOrDefault(new Vector3Int(x + cellPos.x, cellPos.y, z + cellPos.z));
// if (freeCell.roomId != -1)
// {
// if (lastCheckPos.x < x)
// resultWidth = Mathf.Min(x, resultWidth);
// if (lastCheckPos.z < z)
// resultLength = Mathf.Min(z, resultLength);
// }
// lastCheckPos = new Vector3Int(x, 0, z);
// }
// }
// return new Vector3Int(resultWidth, 0, resultLength);
//}
private void OnDrawGizmos()
{
if (_cells != null && _cells.Count > 0)
for (int x = 0; x < _width; x++)
{
for (int z = 0; z < _length; z++)
{
if (_cells.TryGetValue(new Vector3Int(x, 0, z), out var cell))
{
Random.InitState(cell.roomId);
Gizmos.color = Random.ColorHSV();
Gizmos.DrawCube(new Vector3(x * _roomSize.x + _roomSize.x / 2f, 0, z * _roomSize.z + _roomSize.z / 2f), new Vector3(_roomSize.x, 0.1f, _roomSize.z));
}
}
}
}
}
using UnityEngine;
[System.Serializable]
public class DungeonRoomData
{
public GameObject wallPrefab;
public GameObject doorPrefab;
public GameObject floorPrefab;
public GameObject ceilingPrefab;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment