Create Rooms in an House ( Generating )

Hi all.

I have done some Random generation before using crawlers etc. Not always happy how it looked when done. It is just way to random. Then Player around using Spelunky’s system. Which worked good. Start somewhere at the top. Take a Path down and Fill the rest. All with pre-made rooms.

Now I wanna use a similar thing for creating rooms in a house. But also use it to create hallways.
Found some ideas on the net but really have no idea how to approach this. See Below

On first pass, split house space into halls and {blocks of rooms}. Get next big chunk, split it into {hall and chunk} or {2 chunks and hall between them}. On every step, rotate slicing direction by 90 degrees. Stop when {no more big chunks left} or {total hall area reached limit}.

On second pass, split remaining chunks into rooms. Get next big chunk and split it. Skip splitting some not-so-big chunks at random, to have some big rooms.

If any hall is facing much older hall, place wall (or wall with door) there.

Connect rooms with halls directly or through other already-connected rooms.

Ok. No response. But manage to get started on the concept. manage to

  • Create Area size aka Grid
  • Create walls around.
  • Create Big Chunks of section
  • Split Sections into Rooms.

Questions. Should I go this route for rooms or should I go premade rooms. But then how to fit them in!

The reason is likely that this question has nothing directly to do with Unity but more to do with general 2d dungeon creation engineering.

There have been oceans of digital ink spilled on this subject through the years.

The tl;dr of all of them might be “it’s complicated, choose a method that works for you.”

8453543--1121630--Screen Shot 2022-09-20 at 11.36.06 AM.png

1 Like

Yeah. noticed that now. I will Continue to post here on my progress for future people searching in here

1 Like

Hi All. I am stuck at this point. here is the code for anybody to use and maybe optimize and Post back here. Currently it is ending like this. Outer wall to thick if a wall is there. No doors ( Raycast maybe from the middle of the room to each side and look for a hallway? of none is found by each side the room is in the middle of other rooms. Then maybe pick a direction and place a door?

using UnityEngine;
using UnityEngine.Tilemaps;
using System.Collections;
using System.Collections.Generic;

public class DungeonGenerator : MonoBehaviour {



    enum gridSpace { empty, _base, floor, wall, resource };
    gridSpace[,] grid;

    public int roomWidth, roomHeight;

    [Range (0, 6)]
    public int numberOfIterations;

    [Range (1, 4)]
    public int corridorThickness;

    public bool shouldDebugDrawBsp;

    public Tile debugTile,halwayTile,wallTile,doorTile;
    public const int MIN_ROOM_DELTA = 3;



    public Tilemap map;
    public Tilemap roomMap;

    private BspTree tree;
    //public List<Vector4>  roomList = new List<Vector4>();
  
    public List<BspTree> rooms = new List<BspTree>();

    // Use this for initialization
    void Start () {



    }

    void OnDrawGizmos () {
        AttemptDebugDrawBsp ();
        AttemptDebugDrawBspRooms();

    }

    private void OnDrawGizmosSelected () {
        AttemptDebugDrawBsp ();
        AttemptDebugDrawBspRooms();
    }

    void AttemptDebugDrawBsp () {
        if (shouldDebugDrawBsp) {
            DebugDrawBsp ();
        }
    }


    void AttemptDebugDrawBspRooms()
    {
        if (shouldDebugDrawBsp)
        {
            DebugDrawBspRooms();
        }
    }




    public void DebugDrawBsp () {
        if (tree == null) return; // hasn't been generated yet

        DebugDrawBspNode (tree); // recursive call
    }

    public void DebugDrawBspNode (BspTree node) {
        // Container
        Gizmos.color = Color.green;
        // top       
        Gizmos.DrawLine (new Vector3 (node.container.x, node.container.y, 0), new Vector3Int (node.container.xMax, node.container.y, 0));
        // right
        Gizmos.DrawLine (new Vector3 (node.container.xMax, node.container.y, 0), new Vector3Int (node.container.xMax, node.container.yMax, 0));
        // bottom
        Gizmos.DrawLine (new Vector3 (node.container.x, node.container.yMax, 0), new Vector3Int (node.container.xMax, node.container.yMax, 0));
        // left
        Gizmos.DrawLine (new Vector3 (node.container.x, node.container.y, 0), new Vector3Int (node.container.x, node.container.yMax, 0));

        // children
        if (node.left != null) DebugDrawBspNode (node.left);
        if (node.right != null) DebugDrawBspNode (node.right);
    }


    public void DebugDrawBspRooms()
    {

        for (int i = 0; i < rooms.Count; i++)
        {
            if (rooms[i] == null) return; // hasn't been generated yet

            DebugDrawBspNodeRooms(rooms[i]); // recursive call
        }

    }

    public void DebugDrawBspNodeRooms(BspTree node)
    {
        // Container
        Gizmos.color = Color.blue;
        // top       
        Gizmos.DrawLine(new Vector3(node.container.x, node.container.y, 0), new Vector3Int(node.container.xMax, node.container.y, 0));
        // right
        Gizmos.DrawLine(new Vector3(node.container.xMax, node.container.y, 0), new Vector3Int(node.container.xMax, node.container.yMax, 0));
        // bottom
        Gizmos.DrawLine(new Vector3(node.container.x, node.container.yMax, 0), new Vector3Int(node.container.xMax, node.container.yMax, 0));
        // left
        Gizmos.DrawLine(new Vector3(node.container.x, node.container.y, 0), new Vector3Int(node.container.x, node.container.yMax, 0));

        // children
        if (node.left != null) DebugDrawBspNodeRooms(node.left);
        if (node.right != null) DebugDrawBspNodeRooms(node.right);
    }


    private void InitReferences () {
       
        map.ClearAllTiles ();
        roomMap.ClearAllTiles();
        rooms.Clear();
       // roomList.Clear();
        Random.InitState(Random.Range(0,9999999));
    }

    private void GenerateRoomsInsideContainers () {
        BspTree.GenerateRoomsInsideContainersNode (tree);
    }

    private void GenerateContainersUsingBsp()
    {
        tree = BspTree.Split(numberOfIterations, new RectInt(0, 0, roomWidth, roomHeight));


        //Add for Debugging



    }






        private void UpdateTilemapUsingTreeNode (BspTree node) {
        if (node.left == null && node.right == null) {
            for (int i = node.room.x; i < node.room.xMax; i++) {
                for (int j = node.room.y; j < node.room.yMax; j++) {

                    grid[i, j] = gridSpace.wall; //Fill Big Room With Wall. Smaller Room will remove Walls
                  //  map.SetTile (new Vector3Int (i, j, 0), debugTile);
                }
            }

        } else {
            if (node.left != null) UpdateTilemapUsingTreeNode (node.left);
            if (node.right != null) UpdateTilemapUsingTreeNode (node.right);
        }
    }

    private void FillRoomsOnTilemap () {
        UpdateTilemapUsingTreeNode (tree);
    }


   
    void SpawnLevel()
    {
        Debug.Log("Spawn ");


        for (int x = 0; x < roomWidth; x++)
        {
            for (int y = 0; y < roomHeight; y++)
            {


                switch (grid[x, y])
                {


                    case gridSpace.empty:

                        break;

                    case gridSpace.floor:

                        map.SetTile(new Vector3Int(x, y, 0), halwayTile);


                        break;

                    case gridSpace._base:
                        map.SetTile(new Vector3Int(x, y, 0), debugTile);

                        break;


                    case gridSpace.wall:
                        map.SetTile(new Vector3Int(x, y, 0), wallTile);

                        break;
                    case gridSpace.resource:

                        break;
                }
            }
        }
    }


    void WallsBarrier()
    {
        for (int x = 0; x < roomWidth; x++)
        {
            for (int y = 0; y < 2; y++)
            {
                {
                    //if Raam sont fill ?????
                    grid[x, y] = gridSpace.wall;
                }
            }

        }

        for (int x = 0; x < roomWidth; x++)
        {
            for (int y = roomHeight - 1; y > (roomHeight - 3); y--)
            {
                {
                    //if Raam sont fill ?????
                    grid[x, y] = gridSpace.wall;
                }
            }

        }



        for (int x = 0; x < 2; x++)
        {
            for (int y = 0; y < roomHeight; y++)
            {
                {
                    //if Raam sont fill ?????
                    grid[x, y] = gridSpace.wall;
                }
            }

        }

        for (int x = roomWidth - 1; x > (roomWidth - 3); x--)
        {
            for (int y = 0; y < roomHeight; y++)
            {
                {
                    //if Raam sont fill ?????
                    grid[x, y] = gridSpace.wall;
                }
            }

        }

    }



    private void GenerateCorridors()
    {
        // for each parent
        // find their center
        // find a direction and connect these centers
        GenerateCorridorsNode(tree);
    }

    private void GenerateCorridorsNode(BspTree node)
    {
        print('a');
        if (node.IsInternal())
        {
            print('b');
            RectInt leftContainer = node.left.container;
            RectInt rightContainer = node.right.container;
            Vector2 leftCenter = leftContainer.center;
            Vector2 rightCenter = rightContainer.center;
            Vector2 direction = (rightCenter - leftCenter).normalized; // arbitrarily choosing right as the target point
            while (Vector2.Distance(leftCenter, rightCenter) > 1)
            {
                if (direction.Equals(Vector2.right))
                {
                    for (int i = 0; i < corridorThickness; i++)
                    {
                        //map.SetTile(new Vector3Int((int)leftCenter.x, (int)leftCenter.y + i, 0), wallTile);
                        grid[(int)leftCenter.x, (int)leftCenter.y + i] = gridSpace.wall;

                    }
                }
                else if (direction.Equals(Vector2.up))
                {
                    for (int i = 0; i < corridorThickness; i++)
                    {
                        //  map.SetTile(new Vector3Int((int)leftCenter.x + i, (int)leftCenter.y, 0), wallTile);

                        grid[(int)leftCenter.x + i, (int)leftCenter.y]= gridSpace.wall;
                    }
                }
                leftCenter.x += direction.x;
                leftCenter.y += direction.y;
            }
            if (node.left != null) GenerateCorridorsNode(node.left);
            if (node.right != null) GenerateCorridorsNode(node.right);
        }
    }



    public void GenerateDungeon () {


        grid = new gridSpace[roomWidth, roomHeight];
        //set grid's default state
        for (int x = 0; x < roomWidth; x++)
        {
            for (int y = 0; y < roomHeight; y++)
            {
                //make every cell "empty"
                grid[x, y] = gridSpace.floor; //Replace with Empty
            }
        }

       

        //Create Floor
        InitReferences ();
        GenerateContainersUsingBsp ();
        GenerateRoomsInsideContainers ();//Grphics
        FillRoomsOnTilemap();

        //Create Rooms ins Froor Space
        GenerateRoomsUsingBsp(tree);
        GenerateSmallRoomsInsideContainers();
        FillSmaalRoomsOnTilemap();



        FixWalls();
        WallsBarrier();
       // GenerateCorridors();
        SpawnLevel();

        Debug.Log(rooms.Count);

    }





    private void GenerateRoomsUsingBsp(BspTree node)
    {
        if (node.left == null && node.right == null)
        {


                    GenerateRoomsUsingBsp(node.container.x+2, node.container.y+2, node.container.width-2, node.container.height-2);
                    //  map.SetTile (new Vector3Int (i, j, 0), debugTile);


        }
        else
        {
            if (node.left != null) GenerateRoomsUsingBsp(node.left);
            if (node.right != null) GenerateRoomsUsingBsp(node.right);
        }
    }



    private void GenerateRoomsUsingBsp(int startX, int startY, int width, int height )
    {


        Debug.Log(" Creating Room ");
        BspTree newroom = BspTree.Split(2, new RectInt(startX, startY, width-2, height-2));
       // roomList.Add(new Vector4(startX, startY, width - 2, height - 2));
        // public List<PersonsData> Persons = new List<PersonsData>();

        rooms.Add(newroom);
    }

    // private void GenerateSmallRoomsInsideContainers()
   //  {
     //    BspTree.GenerateRoomsInsideContainersNode(tree);
   //  }


    void GenerateSmallRoomsInsideContainers()
    {
        for (int i = 0; i < rooms.Count; i++)
        {
            if (rooms[i] == null) return; // hasn't been generated yet
            BspTree.GenerateSmallRoomsInsideContainersNode(rooms[i]);
        }

    }


     void UpdateTilemapForRoomUsingTreeNode(BspTree node)
    {
        if (node.left == null && node.right == null)
        {
            for (int i = node.room.x; i < node.room.xMax; i++)
            {
                for (int j = node.room.y; j < node.room.yMax; j++)
                {

                    grid[i, j] = gridSpace._base;
                    //  map.SetTile (new Vector3Int (i, j, 0), debugTile);
                }
            }

        }
        else
        {
            if (node.left != null) UpdateTilemapForRoomUsingTreeNode(node.left);
            if (node.right != null) UpdateTilemapForRoomUsingTreeNode(node.right);
        }
    }

    private void FillSmaalRoomsOnTilemap()
    {
        for (int i = 0; i < rooms.Count; i++)
        {
            if (rooms[i] == null) return; // hasn't been generated yet

            UpdateTilemapForRoomUsingTreeNode(rooms[i]);
        }
      
   
    }

    void FixWalls() //Remove if Double Wall
    {

        for (int x = 3; x < roomWidth - 3; x++)
        {
            for (int y = 3; y < roomHeight - 3; y++)
            {

                if (grid[x, y] == gridSpace.wall && grid[x , y+1] == gridSpace.wall && grid[x-1, y] == gridSpace.wall&& grid[x, y-1] == gridSpace._base )
                {
                    grid[x, y] = gridSpace._base;
                }



            }

        }
    }




}
using System;
using UnityEngine;

public class BspTree {

    public RectInt container;
    public RectInt room;
    public BspTree left;
    public BspTree right;

    public BspTree (RectInt container) {
        this.container = container;
    }

    public bool IsLeaf () {
        return left == null && right == null;
    }

    public bool IsInternal() {
        return left != null || right != null;
    }

    internal static BspTree Split (int numberOfIterations, RectInt container) {
        var node = new BspTree (container);
        if (numberOfIterations == 0) return node;


        if (container.width > 25 && container.height > 25) //Min Room Size
        {
            var splittedContainers = SplitContainer(container);
            node.left = Split(numberOfIterations - 1, splittedContainers[0]);
            node.right = Split(numberOfIterations - 1, splittedContainers[1]);
        }
      





        return node;
    }

    private static RectInt[] SplitContainer (RectInt container) {
        RectInt c1, c2;

 
        //if (UnityEngine.Random.Range (0f, 1f) > 0.5f)
            if (container.width < container.height)
          

        {
          
                // vertical
                c1 = new RectInt (container.x, container.y, container.width, (int) UnityEngine.Random.Range (container.height * 0.3f, container.height * 0.7f));
                c2 = new RectInt (container.x, container.y + c1.height, container.width, container.height - c1.height);
            } else {
                // horizontal
                c1 = new RectInt (container.x, container.y, (int) UnityEngine.Random.Range (container.width * 0.3f, container.width * 0.7f), container.height);
                c2 = new RectInt (container.x + c1.width, container.y, container.width - c1.width, container.height);
            }

       
        return new RectInt[] { c1, c2 };
    }

    public static void GenerateRoomsInsideContainersNode(BspTree node)
    {
        // should create rooms for leafs
        if (node.IsLeaf()) {
            //var randomX = UnityEngine.Random.Range(DungeonGenerator.MIN_ROOM_DELTA, node.container.width / 4);
            // var randomY = UnityEngine.Random.Range(DungeonGenerator.MIN_ROOM_DELTA, node.container.height / 4);
            int roomX = node.container.x+2;// + randomX;
            int roomY = node.container.y+2;// + randomY;
            int roomW = node.container.width - 4;// (int) (randomX * UnityEngine.Random.Range(1f, 2f));
            int roomH = node.container.height - 4;//  (int) (randomY * UnityEngine.Random.Range(1f, 2f));
            node.room = new RectInt(roomX, roomY, roomW, roomH);
        } else {
            if (node.left != null) GenerateRoomsInsideContainersNode(node.left);
            if (node.right != null) GenerateRoomsInsideContainersNode(node.right);
        }
    }

    public static void GenerateSmallRoomsInsideContainersNode(BspTree node)
    {
        // should create rooms for leafs
        if (node.IsLeaf())
        {
            //var randomX = UnityEngine.Random.Range(DungeonGenerator.MIN_ROOM_DELTA, node.container.width / 4);
            // var randomY = UnityEngine.Random.Range(DungeonGenerator.MIN_ROOM_DELTA, node.container.height / 4);
            int roomX = node.container.x +1;// + randomX;
            int roomY = node.container.y + 1;// + randomY;
            int roomW = node.container.width - 2;// (int) (randomX * UnityEngine.Random.Range(1f, 2f));
            int roomH = node.container.height - 2;//  (int) (randomY * UnityEngine.Random.Range(1f, 2f));
            node.room = new RectInt(roomX, roomY, roomW, roomH);
        }
        else
        {
            if (node.left != null) GenerateSmallRoomsInsideContainersNode(node.left);
            if (node.right != null) GenerateSmallRoomsInsideContainersNode(node.right);
        }
    }

}

Editor script for Editor folder

using UnityEditor;
using UnityEngine;

[CustomEditor (typeof (DungeonGenerator))]
public class DungeonGeneratorEditor : Editor {


    bool showTiles = true;

    void OnEnable () {

    }

    public override void OnInspectorGUI () {
        DrawDefaultInspector ();



        DungeonGenerator myScript = (DungeonGenerator) target;
        if (GUILayout.Button ("Generate Dungeon")) {
            myScript.GenerateDungeon ();
        }
    }

}