Procedural Dungeon Generator in Unity [TUTORIAL]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello there it's silverly b here and today i'm going to be showing you guys how i did this very easy dungeon generator on unity this 3d dungeon generator is based on a backtracking algorithm and even though these rooms here are 3d you can very easily translate this into a 2d dungeon on this tutorial we'll be focusing mainly on making things easy and simple and on my next tutorial on these dungeons we're going to be focusing on making this more complex and more robust okay so let's begin the first thing we're going to need is to create the rooms that we will be using as our dungeon rooms i'll be using this package right here that is the stylized hand painted dungeon you can find it for free on the unity asset store it's made by l2s arts both the link to this asset on the asset store as well as the link to l2s's portfolio will be left down in the description below so i'll start out by creating an empty game object that will be my room and inside of here i'm going to create an empty game object where i'll put all of the floors that i'll be using we can then enable snapping and just create this room according to what we want to build here we have the room that we're going to be using and it's looking great now the only thing that's left is that we need to make doors to our room and in order to make doors to this room we're going to separate a new game object here that we're going to be calling our entrances inside of it we're going to drag and drop all of these middle walls because this is where the entrances will be positioned we can drag and drop it here we can then just drag and drop the other types of entrances that will be these ones okay so now we have all the objects that we're going to be needing on our room all we need to do now is label them properly and it's very important that the doors and the middle walls be named accordingly and what we're going to be doing is whatever door is here up on the z axis is going to be our up door and also the wall up and whatever door and wall is here on the x-axis we're going to be calling this the right door then this one is going to be the down door and the down wall and this one right here will be the left door and the left wall now that we have renamed these objects what we're going to be doing is just organizing them so they are in this sequence up down right and left we can now start working on the room script we can here create a new folder that we're going to be calling our scripts folder inside of this we're going to create a new chart script that we're going to be calling room behavior we can then open this on visual studio and inside of here we're going to first ask for the walls of our rooms okay so we're going to ask for a public game object array that will be our walls and we're also going to be asking for a public game object array that will be our doors and the way we're going to be organizing this is the first element will be our up door the second one will be r down and then we have right and left this is how this is going to go now we need to create a function here that is going to update our room in order to activate or deactivate the doors and the walls in our room so we're going to create a new function called update room okay now that we have this we're just going to be asking here for a full array that we're going to be calling the status of our room so here we can see which doors are open and which doors are closed the way we're going to do this is whenever we have a true on one of these elements of this status array it means that the door is there that it's opened in that direction and it's going to be following this right here if the element at the zero index is true it means that there is a door over there at the upside of our room so we can go through each and every one of our status elements so status dot blank and inside of here whenever it's true we're going to be setting our doors active so doors at that index dot set active is going to be equal to the status at that index so if it's true the door is going to be active if it's false the door is going to be inactive as for the wall there it's going to be the opposite of the door so here this is going to be the opposite of the current status at that index so if it's true our walls will be inactive and if it's false our walls will be active we can save this and in order to test this we can just create one more thing here that we can erase later this will be a full array and this is going to be our test status and this is just to check if this update room function is working and we're going to call here update room with our test status we can then save this head into unity and check to see here if this is working on our room mate object we're going to drag and drop this room behavior inside of here it's going to ask us for our walls we can then just lock this here we select from the up wall down to the left one we drag and drop it here and we select all of our doors drag and drop it here as well and then we adjust this test status we say that its size is four and inside of here we tell which doors we want to be opened so for instance we want the up door and also the right one to be opened when we hit play we can see then that if we go back into our scene here we have the up and the left door opened now all we have to do is we have to check how this works how this room here would work with other rooms around this is the dungeon that we're trying to create now that we have all of these rooms right here what we need to do is first we unlock this on the inspector and then we can adjust all of these rooms so that they open and close the doors that they need to open and close in order to make a path here and we can hit play to see if this works inside of our scene we can then see that this would be creating our dungeon path so this is what we're going to be doing but instead of just going here on our test status and working which doors are opened and which parts are closed we're going to be generating this maze like dungeon procedurally we can then start working on the algorithm for the generation of the dungeon so here on our assets we can create a new folder that we're going to be calling our prefabs folder and as always we can delete all of this and just drag and drop the room inside of our prefabs folder inside of our scripts folder we can create a new c-sharp script that will be our dungeon generator in order to create the script we first need to understand how the generation of our dungeons is going to be working we're going to be creating the dungeon using a backtracking algorithm to generate our dungeon with a maze-like path the algorithm we're going to be using is also called that first search or dfs in order to generate a maze using the depth first search algorithm we first start with a grid of cells then it will randomly pick a path through this grid and go as far as it can through the unvisited cells until it has nowhere else to go then it will start going back on its own path looking for other paths to take this is why it's called depth first search or backtracking algorithm the first step is to choose a starting point this will be the first cell that will sprout the mace from then on it must check all the unvisited neighbors of the current cell and randomly choose which is going to be the next cell if the current cell has no unvisited neighbors that it can go to then it backtracks to the previous cell on the path this goes on until it backtracks to the less cell of the path which was our starting point and there we have it our maze now that we have an overview of the algorithm we can then just start working on it first let's create a function here that will be generating the grid and the mace so we're going to create here a void maze generator this will be creating our maze another function we are going to be creating here is the function to check the neighbors that we have around so here where be our check neighbors function and for now this is it the main thing we're going to be creating is a class that we're going to be calling cell and this is going to be holding the information off every cell inside of our maze and the first bit of information that we need is to know if this has been visited or not so this is going to be a public bowl that will be called visited and this is going to start as false and inside of here we're going to have a public bull array that will be the status of the cell if the cell is open up down left or right it's going to be a new bull of four slots another important thing is that in here if you take a look you can see that first this is going to be either true or false and here the status is also a boo and it's going to be either true or false so instead of using a boo we can just use a binary number with 5 bits to represent each of these bools that we're going to be needing in our cell this could be done and if you know how to work around binary digits just transform this into a 5 bit binary digit but because here on the tutorial it's meant to be clearer i prefer to work like this so that we just need to understand that the status means up down right and then left and we don't need to keep track of binary digits and their position now that we've created our cell here we can start asking for the variables that we're going to need in order to generate this dungeon path the first thing we're going to need is the size of this grid we're going to be asking for a vector 2 that will be our size and the other thing we're going to be asking for is for our start position so the position where our dungeon will start and it's normally going to be zero you can use whatever other number here but in our case here it's probably going to be zero another thing we're going to need is a list of cells that will be our board so for now this is all we're going to be needing and let's begin working on our maze the first thing we need is to start our board as a new list of cells and now what we're going to do is we're going to create this board with all the cells that it must have so inside of here we're going to go through every size that we have so here size dot x and we're also going to go through every y that we have going through all of this we're going to be adding a new cell inside of our board so once we've done this what we're going to be doing is we're going to be creating a new current cell that is going to be keeping track of which position we're at and this is going to be starting as the start position and we're going to be creating a path that is going to be keeping track of the path that we've made until the cell that we're currently at here it's going to be a stack of end that we're going to be calling path and this is going to be starting as a new stack of ants we're going to create here a variable that we're going to be calling k this is just to keep track at which loop we're at because we're going to be creating a while and we don't want this to go indefinitely so for now we're going to be doing is we're going to be creating a loop here that y okay is less than a thousand and this is because a thousand is going to be sufficient for us in our case we're not going to be making giant mazes but this might need a bigger number if your dungeon is supposed to be bigger and while this is going on we're going to first increase k and now we need to work on the actual algorithm that we're going to be using first what we're going to be doing is we're going to get the current cell so board current cell and we're going to say that it has been visited so now what we need to do is we need to check the cell's neighbors and inside of here this check neighbors we've said that this is a void this is not going to be a void this is going to be returning to us the neighbors of the cell let's create this function first this is going to be returning to us a list of all the neighbors of the cell and in order to check the neighbors of a cell we need to know which cell we're currently checking so we need the position of the cell all we need to do is we need to create a list of ends that we're going to be calling the neighbors and we're going to be returning this neighbors so what we need to check here is first we need to check the up neighbor of our cell and then we're going to be checking the down neighbor the right neighbor and the left neighbor in order to check if the up neighbor of our cell is available we need to check if it exists so here we need to check if the cell position minus the size dot x is greater or equal to zero so this here is going to be checking if it's not on the first row therefore if it has an up neighbor and now the other thing we need to do is to check if this up neighbor that we know exists hasn't been visited and is available for us in order to know that we need to check on the board we're going to get the cell minus the size dot x in order to get the up neighbor and because the size of x is a vector 2 and it is a float we need to round this to an end so here we're going to be using a floor to inch in order to get the correct cell and this is not supposed to have been visited so has been visited has to be false inside of here if all of this is correct we can then add the position of this neighbor that is right here we can add this to our neighbors now we just need to do this to the down neighbor and the right and the left and in order to do so it's going to be pretty similar to this one but here instead of a minus this is going to be a plus and this has to be lesser than the size of the board in order to not be on the last row so board dot count and here it's going to be a plus and also here we're going to be adding a plus instead of a minus now in order to create the right neighbor it's going to be a little bit different because now it can't be on the utmost right column and in order to check what we're going to be using is first here it has to be a percent in order to get the remainder of this division and this is going to be 0 when it's on the first column so if we were to check if this is different than 0 we can check on our left neighbor but because it's our right neighbor if we add one here this now has to be different than zero because normally if it's the rightmost column of our board then it's going to be equal to size dot x minus one then the remainder here is going to be size dot x minus one and if we just add one before we can check to see if this is different than zero and now inside of here this is going to be just plus one because it's the neighbor to the right of our cell we can copy this and just as i've said here we can erase this part and check to see if this is different than zero because if it is then we're not on the leftmost column of our board and inside of here instead of plus it's going to be minus and here instead of plus size x it's going to be plus one and here we can replace those four minus one so now we're correctly checking all of our neighbors and once we've done this we can save this into a list of our own okay so list end of the neighbors is going to be equal to whatever we get from the function true check the neighbors and it also requires us to give the position of the current cell so we just need to pass this current cell that we're working on now if the neighbor's count is equal to zero this means that there are no available neighbors what we're going to be doing is we're going to be checking the path that we have so if path dot count is equal to zero we're going to be breaking out of this loop because we've reached the less cell on this path and if this is not equal to zero we're going to go back to the last cell so else we're going to get the current cell as the path dot pop so the current cell will now be the last cell added into our path okay now if we actually have neighbors if neighbors dot count is greater than zero what we're going to be doing is first we need to add this current cell to our path so path dot push and we're going to push the current cell so now we just need to choose a neighbor f random so the new cell is going to be a random neighbor so neighbors we're going to be getting one of them at random so random.range it's going to be zero to neighbors dot out now we're getting a new cell that is randomly chosen between our neighbors all we need to do is check if this goes up down left or right if our new cell is greater than our current cell then this path is going down or this path is going right in order to check if it's going right all we need to do is to check if this new cell -1 is equal to the current cell if it is then we are sure that this is going right if this is going right then we need to access our cell on our current cell we are going to say that its status on the right position is going to be equal to true and we're going to say that the current cell now is equal to our new cell and we can copy this part over here we're going just to say that on our new cell the left path is open now if this is false then we can copy this part and just replace the numbers with the appropriate numbers our current cell is open downwards and the next cell is going to be open upwards now if this is smaller then we can do the same thing in order to check here but instead of down or right it's going to be up or left in order to check if this goes left we can ask if new cell plus 1 is equal to the current cell then it goes left if this goes left then the current cell opens to the left and the next cell is going to be opening on the right now if this is false then here it's going to be opening up and here it's going to be opening down now that we've generated the maze what we can do is start working on generating the dungeon in order to create our dungeon we're going to be reading our board in order to see how each cell is going to be behaving and if one of these cells has been visited we are going to be creating the cell and updating its room using the update room here in order to pass down its status first what we're going to be doing here is deleting this we can then just delete the start function we're just going to need this update room function and this is going to be public you can save this now once we've generated our maze inside our board we can call the void generate dungeon inside of here we're going to be creating each room that we've defined here on our maze generator and what we're going to be doing is at the start we're going to be calling the maze generator and once we've run this maze generator we're going to be calling this generate dungeon okay so inside of here we're also going to need a reference to the prefab that we're going to be using in this case it's going to be a game object that will be our room so in order to instantiate our board properly what we're going to need as well is a vector 2 that will be our offset this is going to be the distance between each room inside of here on our generate dungeon we're going to go through all of the rows and all of the columns of our board we can then instantiate our room and it's going to be instantiated in a position according to the offset that we're going to be setting the position is going to be i multiplied by the offset dot x on the y it's going to be zero and on the z here it's going to be j multiplied by offset dot y and sorry this here is going to be new vector3 and here it's going to be quaternion dot identity and its parent is going to be the current game object another thing we need to do is we need to call this function over here with the status of this room okay so inside of here we can use a var new room and this is going to be equal to this dot get component we're going to be getting the room behavior component and to this new room we can call the update room and pass the status of this cell okay so it's going to be board i plus j multiplied by size dot x and this here has to be an int so let's use math f dot floor to end what we're going to be passing is not just the cell but the status of the cell we can save this this should be working correctly let's check to see on unity if this works so here we have our room we can just delete this we can create our new empty game object that will be our generator we can then reset its transform and use this dungeon generator here first we're going to drag and drop this prefab into our room here it's going to have a small size in order for us to check to see how this looks like and to check the offset that it's supposed to have we're going to create this room right here we're going to duplicate this and check to see what the offset is supposed to be and we can see here that the offset is supposed to be off six on our generator we can inform that the offset is of six and our start position can be zero we can then hit play and see if this is working so what is happening is the up and down part of our board is a bit mixed up and this is because right here this should be minus because we're treating it as if the topmost rule in our board is the first row and if this is positive then the lowest row in our board is going to be the first numbers we can also then just name each of our rooms according to their position so we're going to be adding here a space and also the eye position of our room plus a spacer plus the j position of our room saving this we can head into unity and see if this is working this time hitting play we can go back to our scene and as we can see it looks like it's working correctly so if we were to start here we can then just go through all of these rooms and we have here our little dungeon as of now we've created a little bit of a maze dungeon another thing we can do in order to make this more interesting and not just be limited by the square shape is instead of just going through this loop and going through every single one of our cells what we can do is we can establish a blessed cell that here in our case will be the last cell inside of our board okay and if here right after we've considered the cell as visited if the cell is equal to board dot count -1 this means the last cell of our board then we're going to be breaking out of this loop now all we have to do is when we go through here generating our dungeon what we need to check is if this room has been visited we're going to get the current cell that is going to be equal to this right here and instead of using this we can just use the current cell if this current cell has been visited then we can instantiate this cell so if it's visited then we can instantiate it if it hasn't been visited then we're not going to be instantiating this cell you can save this heading to unity and check to see how this works with a bigger board so let's go with a 10 by 10 board we can hit play and check to see how this works inside of our scene we can see now that our dungeon has a more interesting shape and it goes from the starter cell up until the last cell and you just need to create your player inside of the starter cell and make i don't know a treasure room or a boss fight here on the last room and here you have it [Music] this is a very simple algorithm that can be used to generate other types of stuff here i'm showing how to generate a dungeon but you can use this to generate a maze to generate levels to generate lots of stuff and it's a great base to build up from on my next video i'll be taking a look at how to take this to the next level and make it a bit more complex but a bit more interesting as well if this video was helpful to you don't forget to leave a like if you have any questions leave them down in the comments below and for more videos such as this one please consider subscribing if the next video is already out you can go watch it right now and take test dungeon to the next level thank you so much for watching bye
Info
Channel: SilverlyBee
Views: 2,871
Rating: undefined out of 5
Keywords: tutorial, tileset, unity, unity tutorial, unity board, boards, depth first search, backtracking algorithm, algorithm, maze generator, dungeon generator, dungeon, dungeons, unity dungeon, unity dungeons, path generator, generator, unity3d, unity 3d, unity 3d tutorial
Id: gHU5RQWbmWE
Channel Id: undefined
Length: 33min 18sec (1998 seconds)
Published: Tue Sep 21 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.