During the Part2, we updated the *dungeonMap* to have all coordinates in the same room share the same numerical value. However, all rooms are unconnected and that would make a pretty boring dungeon. We’ll correct that!

Add corridors

The goal of this step is to add corridor and make sure that all rooms are connected together and are all accessible. In the following image, you can see an updated version of the previously created dungeon with corridors. Each room now have a corridor starting from a random position in it(C#_Start) going to another random position in a random room/corridor(C#_End).

Here is the whole *CreateDungeonHalls()* method that update the *dungeonMap* with added corridors. The code below will be split in 3 part below and explained individually.

// Create a hall starting from each room, connecting to another room or corridor static private int[,] CreateDungeonHalls(int[,] _dungeonMap, int _sizeX,int _sizeZ, int nbrRoom) { int _x1; // x coordinate of the starting position int _z1; // z coordinate of the starting position int _x2; // x coordinate of the ending position int _z2; // z coordinate of the ending position // Start a corridor from each room for(int _curRoomNbr = 1; _curRoomNbr <= nbrRoom; _curRoomNbr++) { int nbrRoomsTry = 0; // Counter is used to avoid looping forever if there is a bug in the program. int _nbrRoomsTryMax = 5000; // Find a random coordinate in the room with number _curRoomNbr _x1 = Random.Range (1, _sizeX-1); _z1 = Random.Range (1, _sizeZ-1); // Find a random position with the current room number while(_dungeonMap[_x1,_z1] != _curRoomNbr && nbrRoomsTry < _nbrRoomsTryMax) { _x1 = Random.Range (1, _sizeX-1); _z1 = Random.Range (1, _sizeZ-1); nbrRoomsTry++; } nbrRoomsTry = 0; // Find a random coordinate in any room/corridor that isn't the first room _x2 = Random.Range (1, _sizeX-1); _z2 = Random.Range (1, _sizeZ-1); // Find a random position in a different room or corridor created from a previous room while((_dungeonMap[_x2,_z2] == 0 || _dungeonMap[_x2,_z2] == _curRoomNbr) && nbrRoomsTry < _nbrRoomsTryMax) { _x2 = Random.Range (1, _sizeX-1); _z2 = Random.Range (1, _sizeZ-1); nbrRoomsTry++; } // Difference between both coordinates on each axis. This is used to find direction of corridor to create int _diffX = _x2 - _x1; int _diffZ = _z2 - _z1; int _xDirection = 1; // Coefficient used to loop in the right direction on x axis(-1 or 1) int _zDirection = 1; // Coefficient used to loop in the right direction on z axis(-1 or 1) // Evaluate direction of the corridor on the X and Z axis if(_diffX != 0) { _xDirection = _diffX/Mathf.Abs(_diffX); // 1 = Left to Right (->) ... -1 = Right to Left (<-) } else { _xDirection = 0; // No horizontal corridor if they are aligned on X axis } if(_diffZ != 0) { _zDirection = _diffZ/Mathf.Abs(_diffZ); // 1 = Top to Bottom( \/ ) ... -1 = Bottom to Top ( /\ ) } else { _zDirection = 0; // No vertical corridor if they are aligned on Z axis } // randomize corridor portion and height int _hallWidth = Random.Range (4,10); int _hallHeight = Random.Range (4,10); //Create vertical part of the hall for(int i = _x1; i != _x1 + _xDirection*_hallWidth; i += _xDirection) { for(int j = _z1; j != _z2; j += _zDirection) { if(i >= 0 && i < _sizeX && j >= 0 && j < +_sizeZ) // Make sure that the index is within the map range { if(_dungeonMap[i,j] == 0) // Only modify empty position, not rooms position { _dungeonMap[i,j] = -1; // Write corridor as -1 in the dungeonMap } } } } //Create horizontal portion of the hall for(int i = _x1; i != _x2; i += _xDirection) { for(int j = _z2; j != _z2 + _zDirection*_hallHeight; j += _zDirection) { if(i >= 0 && i < _sizeX && j >= 0 && j < +_sizeZ) // Make sure that the index is within the map range { if(_dungeonMap[i,j] == 0) // Only modify empty position, not rooms position { _dungeonMap[i,j] = -1; // Write corridor as -1 in the dungeonMap } } } } } return _dungeonMap; // The _dungeonMap contains 0 for non-room, -1 for corridor and N for rooms }

¸

First part of the method (line 10-38) simply find a random position to start a corridor from each room and an ending position in another room or in a previously added corridor.

// Start a corridor from each room for(int _curRoomNbr = 1; _curRoomNbr <= nbrRoom; _curRoomNbr++) { int nbrRoomsTry = 0; // Counter is used to avoid looping forever if there is a bug in the program. int _nbrRoomsTryMax = 5000; // Find a random coordinate in the room with number _curRoomNbr _x1 = Random.Range (1, _sizeX-1); _z1 = Random.Range (1, _sizeZ-1); // Find a random position with the current room number while(_dungeonMap[_x1,_z1] != _curRoomNbr && nbrRoomsTry < _nbrRoomsTryMax) { _x1 = Random.Range (1, _sizeX-1); _z1 = Random.Range (1, _sizeZ-1); nbrRoomsTry++; } nbrRoomsTry = 0; // Find a random coordinate in any room/corridor that isn't the first room _x2 = Random.Range (1, _sizeX-1); _z2 = Random.Range (1, _sizeZ-1); // Find a random position in a different room or corridor created from a previous room while((_dungeonMap[_x2,_z2] == 0 || _dungeonMap[_x2,_z2] == _curRoomNbr) && nbrRoomsTry < _nbrRoomsTryMax) { _x2 = Random.Range (1, _sizeX-1); _z2 = Random.Range (1, _sizeZ-1); nbrRoomsTry++; }

Second part of the method (line 40-64) calculate the difference between the start/end position on the X and Z axis. It also calculate a coefficient to increment in the good direction while creating the corridor.

// Difference between both coordinates on each axis. This is used to find direction of corridor to create int _diffX = _x2 - _x1; int _diffZ = _z2 - _z1; int _xDirection = 1; // Coefficient used to loop in the right direction on x axis(-1 or 1) int _zDirection = 1; // Coefficient used to loop in the right direction on z axis(-1 or 1) // Evaluate direction of the corridor on the X and Z axis if(_diffX != 0) { _xDirection = _diffX/Mathf.Abs(_diffX); // 1 = Left to Right (->) ... -1 = Right to Left (<-) } else { _xDirection = 0; // No horizontal corridor if they are aligned on X axis } if(_diffZ != 0) { _zDirection = _diffZ/Mathf.Abs(_diffZ); // 1 = Top to Bottom( \/ ) ... -1 = Bottom to Top ( /\ ) } else { _zDirection = 0; // No vertical corridor if they are aligned on Z axis }

Last part of the method (line 66-101) start by calculating a random width and height for the new corridor. The map is then updated with the vertical portion of the corridor first and the horizontal portion of the corridor second. Corridor are a simple corner for now. It would be possible to add randomness/more corner or connect them in a different way by modifying this part. It would also be possible to customize the corridors width/height depending on the room size/distance between rooms and such.

To create the corridors, we take the previously evaluated direction on the X axis and step toward that direction with the corridor width. This corridor is filled on the Z axis from z1 to z2. Same thing is done with the horizontal part of the corridor.

// randomize corridor portion and height int _hallWidth = Random.Range (4,10); int _hallHeight = Random.Range (4,10); //Create vertical part of the hall for(int i = _x1; i != _x1 + _xDirection*_hallWidth; i += _xDirection) { for(int j = _z1; j != _z2; j += _zDirection) { if(i >= 0 && i < _sizeX && j >= 0 && j < +_sizeZ) // Make sure that the index is within the map range { if(_dungeonMap[i,j] == 0) // Only modify empty position, not rooms position { _dungeonMap[i,j] = -1; // Write corridor as -1 in the dungeonMap } } } } //Create horizontal portion of the hall for(int i = _x1; i != _x2; i += _xDirection) { for(int j = _z2; j != _z2 + _zDirection*_hallHeight; j += _zDirection) { if(i >= 0 && i < _sizeX && j >= 0 && j < +_sizeZ) // Make sure that the index is within the map range { if(_dungeonMap[i,j] == 0) // Only modify empty position, not rooms position { _dungeonMap[i,j] = -1; // Write corridor as -1 in the dungeonMap } } } } } return _dungeonMap; // The _dungeonMap contains 0 for non-room, -1 for corridor and N for rooms }

Now that the map *dungeonMap* is updated with all the rooms and corridors, we need to find a list of all wall to create and instantiate them in the dungeon scene. This will be the topic of the next 2 posts.

Update : I’m currently updating the way I do part 4 and 5. They are accessible on the Dungeon Grind repository on GitHub but, they can’t be buggy or in an imperfect state. You can however look for inspiration on how to continue. The most important file is DungeonGenerator.cs and DungeonManager.cs can be useful.

Pingback: Dungeon Grind – Procedural Dungeon Generation Tutorial | LearningGeek Blog