Hello! I’m working on a top down RPG in the style of the old Final Fantasy games or Stardew Valley, and I’m trying to set up a procedural dungeon generator but I’m running into an issue I can’t figure out with my code.
Sorry in advance for such a long post but I’m super stuck on this.
Here is my generator code:
public void StartRoomGeneration()
{
// This starts the process of generating a dungeon
ClearRooms();
GenerateStarterRoom();
ConnectRoomsList();
}
public void GenerateStarterRoom()
{
// Generates a starter room for a new dungeon
Debug.Log("<color=blue>GenerateRooms called...</color>");
GameObject starterRoom = null;
starterRoom = Instantiate(starterRoomPrefab);
SetRoomParent(starterRoom);
starterRoom.transform.localPosition = Vector3.zero;
roomList.Add(starterRoom);
}
private void ConnectRoomsList()
{
// Generates rooms for all rooms on the roomList and closes connections that can't find a match
foreach (GameObject room in roomList)
{
DungeonRoom dungeonRoom = room.GetComponent<DungeonRoom>();
generateRooms = GenerateRooms(dungeonRoom);
StartCoroutine(generateRooms);
}
}
private IEnumerator GenerateRooms(DungeonRoom room)
{
Debug.Log("<color=orange>Checking connections for room: </color><color=blue>" + room.gameObject.name + "</color>");
for (int connectionIndex = 0; connectionIndex < room.RoomConnections.Count; connectionIndex++)
{
if (room.RoomConnections[connectionIndex].IsOpen)
{
Debug.Log("<color=green>Open connection found at index: </color><color=blue>" + connectionIndex + "</color>");
GameObject adjacentRoom = null;
bool validRoomFound = false;
// This shuffles the possibleRooms list so that it's random but every entry only gets tried once
for (int i = 0; i < possibleRooms.Count; i++)
{
int j = Random.Range(i, possibleRooms.Count);
GameObject t = possibleRooms[i];
possibleRooms[i] = possibleRooms[j];
possibleRooms[j] = t;
}
for (int possibleRoomIndex = 0; possibleRoomIndex < possibleRooms.Count; possibleRoomIndex++)
{
adjacentRoom = Instantiate(possibleRooms[possibleRoomIndex]);
// After the room is Instantiated, it should be moved to the desired location with the desired offset
SetRoomParent(adjacentRoom);
adjacentRoom.transform.localPosition = Vector3.zero;
DungeonRoom adjacentDungeonRoom = adjacentRoom.GetComponent<DungeonRoom>();
Transform targetTransform = room.RoomConnections[connectionIndex].transform;
bool validConnection = false;
int connection = -1;
for (int i = 0; i < adjacentDungeonRoom.RoomConnections.Count; i++)
{
float xOffset = adjacentRoom.transform.localPosition.x - adjacentDungeonRoom.RoomConnections[i].gameObject.transform.localPosition.x;
float yOffset = adjacentRoom.transform.localPosition.y - adjacentDungeonRoom.RoomConnections[i].gameObject.transform.localPosition.y;
//Debug.Log("<color=green>room x: " + adjacentRoom.transform.localPosition.x + " waypoint x: " + adjacentDungeonRoom.roomWaypoints[0].waypoint.gameObject.transform.localPosition.x + "</color>");
//Debug.Log("<color=green>room y: " + adjacentRoom.transform.localPosition.y + " waypoint y: " + adjacentDungeonRoom.roomWaypoints[0].waypoint.gameObject.transform.localPosition.y + "</color>");
//Debug.Log("<color=green>xOffset: " + xOffset + " yOffset: " + yOffset + "</color>");
adjacentRoom.transform.localPosition = targetTransform.localPosition + new Vector3(xOffset, yOffset, 0);
// collisions should be checked next, once the room is in position
yield return new WaitForFixedUpdate();
// if the location is valid, the connection is set to valid and the room's connection index is stored
if (adjacentDungeonRoom.ValidLocation)
{
connection = i;
validConnection = true;
break;
}
}
// if placement is invalid, delete the room
// else, keep the room and mark waypoint as complete
if (validConnection)
{
Debug.Log("<color=green>Valid location and connection found!</color>");
validRoomFound = true;
room.CloseConnection(connectionIndex);
adjacentDungeonRoom.CloseConnection(connection);
break;
}
else
{
Debug.Log("<color=orange>Invalid location, destroying room...</color>");
DestroyImmediate(adjacentRoom);
validRoomFound = false;
}
}
if (validRoomFound)
{
Debug.Log("<color=orange>Valid location, adding room </color><color=blue>" + adjacentRoom.gameObject.name + "</color><color=orange> to newRoomList...</color>");
newRoomList.Add(adjacentRoom);
}
else
{
Debug.Log("<color=orange>No valid rooms found, closing waypoint </color><color=blue>" + connectionIndex
+ "</color><color=orange> for room </color><color=blue>" + room.gameObject.name + "</color>");
room.CloseConnection(connectionIndex);
}
}
}
if (newRoomList.Count > 0)
{
Debug.Log("<color=#2697ab>newRoomList has rooms to add...</color>");
foreach (GameObject newRoom in newRoomList)
{
roomList.Add(newRoom);
}
newRoomList.Clear();
ConnectRoomsList();
}
else
{
Debug.Log("<color=#2d2a82>No new rooms, dungeon is complete!</color>");
}
}
Just to walk you through my current setup, the starterRoom is a room with 4 connections, one in each direction, and then my possibleRooms include 6 rooms: 1 dead end room for each direction, and 2 hallways that have 2 connections (examples in the screenshot below).
Green boxes represent colliders, and ignore the fact that the dead end rooms don’t look like they connect (need to redo their art).
So the main issue I’m having is that for a reason that I can’t pin down, when a room get’s placed, it seems that something funny is going on with the roomList because then it seems like it won’t place down that room again. For example, the hallways (which I have 2 of in the possibleRooms list, as seen above) only ever get placed twice, no matter how many times I run the generator. So it seems that each room is only ever getting place ones. I tested this by making the possibleRooms list consist of only the two hallway rooms, and sure enough, they both got placed and then every other connection was closed off saying it couldn’t find a matching room.
This is not my intention and I can’t seem to figure out why that’s happening. I’m sure there’s some logic that’s off somewhere in my code but I can’t seem to figure it out.
The other issue I’m having is that for whatever reason, the hallways can’t seem to find a fitting room to their far connection, when in fact, the dead end rooms should be working just fine for that. I think this is a separate issue, because in the example above, the north and east dead end rooms aren’t even being used, but they aren’t connecting to the hallways for whatever reason.
I’m sorry for such a long post, but I’ve been working on this for a while now and I genuinely can’t figure it out so if anyone has any ideas I would love to hear them! Thanks!