I have been working on procedural generating levels. I have created boxes who have a spawn depending on their opening.
If a box has a left spawn. the left spawn will know that it needs to create at least 1 door on the right. This seems to be working, but for some reason, after a while, the rooms start stacking on top of each other. even tho my code does not allow this?
Could this be because the walls are not in the perfect symmetry of eachother? Since I want to have broader and different kind of levels I thought having only the spawn points align would be enough?
This is how the level starts 4 different pathways.

Still going good

Still good

As you can see every entrance from the Starting floor have been blocked.
After this they keep stacking op top of eachother giving no end to the generation of levels.

Room Spawner
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
// 1 --> need bottom door
// 2 --> need top door
// 3 --> need left door
// 4 --> need right door
private RoomTemplates templates;
private int rand;
private bool spawned = false;
void Start(){
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.5f);
}
void Spawn(){
if(spawned == false){
if(openingDirection == 1){
// Need to spawn a room with a BOTTOM door.
rand = Random.Range(0, templates.bottomRooms.Length);
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
} else if(openingDirection == 2){
// Need to spawn a room with a TOP door.
rand = Random.Range(0, templates.topRooms.Length);
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
} else if(openingDirection == 3){
// Need to spawn a room with a LEFT door.
rand = Random.Range(0, templates.leftRooms.Length);
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
} else if(openingDirection == 4){
// Need to spawn a room with a RIGHT door.
rand = Random.Range(0, templates.rightRooms.Length);
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("SpawnPoint")){
if(other.GetComponent<RoomSpawner>().spawned == false && spawned == false){
// spawns walls blocking off any opening !
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
Destroy(gameObject);
}
spawned = true;
}
}
}
}
Destroyer tag
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destroyer : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other ){
Destroy(other.gameObject);
}
}
Roomtemplates
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
public List<GameObject> rooms;
}
So I came up with the following solution:
(Limit - if a room is already spawned it is still possible that it might get surrounded by other spawning rooms so its doors get blocked)
Have a proper enum flag type
this allows you to choose one or multiple values from the flag via the Inspector.
Change your
RoomTemplatescript likethis gives direct access to the values of
RoomSpawneron the prefabs.Use the flag instead of the int for defining the next door direction on the prefabs.
Then everytime spawing a new room it ads its own positions to the
occupiedPositionsso no other room can be spawned here anymore.Additionally check in which directions the room that is about to be added can even go and only pick a random room from that list using
Linq Where.If there is no way left to go use the closed Room instead. (You could ofcourse still add it to the prefab lists if you also want the possibility of a randomly picked closed room)