Procedural Generation Test

Engine | Unity

In this test, I wanted to try to polish up my skills with procedural generation. I found a great article on how The Binding Of Isaac did it, by boristhebrave. After finding this, I tried to make my own based upon the 6 key points in the algorithm:

  • Find the neighbor space
  • If there is already a neighbor in the space, stop
  • If the neighbor has more than one filled neighbor, stop
  • If we hit the max rooms, stop
  • Random chance to stop
  • Else, pass the neighbor to the queue of rooms to add

I started out by making some initial variables, constants, and setting up arrays

    //the smaller these numbers are, the closer the rooms
    float cellW = 10f, cellH = 10.05f;
    float W = 1, H = 1;

    //max size of room array, also used in Initalize
    [SerializeField]
    int[] rooms = new int[77];

    //the queue of rooms to be checked
    [SerializeField]
    List<int> queue = new List<int>();

    [SerializeField]
    GameObject[] RoomsHolder = new GameObject[77];

    [SerializeField]
    int currentNumberRooms = 0;
    [SerializeField]
    int maxRooms;

    //constants for each cardinal direction
    const int UP = 10, DOWN = -10, LEFT = -1, RIGHT = 1;
    //constant for the start of the rooms
    const int STARTING_ROOM = 44;

    int randNoUse;

    public Sprite[] RoomTypes;
    public GameObject RoomTemplate;
    public GameObject Door;

    //the bool to start the room generation
    [SerializeField]
    bool startedRoomGen = false, startedDoorGen = false;

After that I created an initialize function that sets resets each room in the array to 0. I call it in Start, along with creating an array of random rooms to stop generation at.

void Initialize()
    {
        //make each number in the array 0
        for (int i = 0; i < rooms.Length; i++)
        {
            rooms[i] = 0;
        }
    }

private void Start()
    {
        startedRoomGen = true;

        //Initialize our variables
        Initialize();

        randNoUse = Random.Range(1, rooms.Length);

        //number of rooms in the map
        maxRooms = Mathf.RoundToInt((Random.Range(2, 8) + 8) * 2.45f);
        RoomCheck(STARTING_ROOM);
    }

Next, in the update function, I check if there is a room in the queue. If there is, I grab the first item in the queue, and check it’s neighbors to see if they fit the above criteria. If they do, I create the room. Finally, no matter what, I remove the room from the queue.

if (startedRoomGen)
        {

            //if there is something in the queue
            if (queue.Count > 0)
            {
                int roomToCheck = queue[0];
                
                if (roomToCheck + UP < rooms.Length)
                {
                    if (rooms[roomToCheck + UP] == 0)
                    {
                        
                        RoomCheck(roomToCheck + UP);
                    }
                }

                if (roomToCheck + DOWN >= 0)
                {
                    if (rooms[roomToCheck + DOWN] == 0)
                    {
                        RoomCheck(roomToCheck + DOWN);
                    }
                }

                if (roomToCheck + LEFT >= 0)
                {
                    if (rooms[roomToCheck + LEFT] == 0)
                    {
                        RoomCheck(roomToCheck + LEFT);
                    }
                }
                if (roomToCheck + RIGHT < rooms.Length)
                {
                    if (rooms[roomToCheck + RIGHT] == 0)
                    {
                        RoomCheck(roomToCheck + RIGHT);
                    }
                }

                MakeRoom(roomToCheck);


                //remove the first item from the queue
                queue.RemoveAt(0);
            }
        }

The RoomCheck function firstly check if there is a compatible neighbor. If there is, then it checks if the generator reached it’s room limit. Once it passes that check, there