Basic 2D Dungeon Generation

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
cool alright it is 3 p.m. so let's put up our our picture of what we're going to be doing so here we have an image of our project for today so this is we're going to be doing some basic 2d dungeon generation and so what I'm going to be doing with this is piggybacking off a project that I did a while ago along with the other members of the content team unity called 2d roguelike where you made like a little tiny 2d roguelike game over the course of 13 videos and if you're interested in that you can check it out on unity 3d comm slash learn and the package is on the asset store so we're going to use the artwork from that and a couple of scripts and of course this theme of Dungeon generation is very much inspired by the roguelike genre I'm a big fan of that genre and and it's sort of a lot of people have been getting into it lately so I thought it would be a fun fun topic to talk about now something that I'll say at the top here is that there are so many different ways to approach generating a dungeon and they're really or just generating any content procedurally so it really varies based on your game what you're trying to achieve the feeling you want to create and so I would not say there is like any right or wrong way to do this it's as much an art as it is a science so just take that for what it's worth right here at the top I'm going to show you one approach I'm trying to keep it as simple as possible I will say this session is going to run long in my run throughs where I wasn't stopping to take any questions it took about 70 minutes and so I figured we're going to run for at least an hour and a half another thing that's going to be a little different about this session is that I am NOT going to be typing everything out live I have like four scripts to go through and so instead I've got commented versions of the scripts that I'm just going to walk through and talk you guys through on screen so let me pull up a finished version of what we're going to create and show you guys an example you guys should be now seeing the unity editor somebody just give me a thumbs up to let me know that you can see that and what I'm going to do is I'm going to play the scene and I'm going to pause so that we can see in the scene view what has been generated here so the here we've got a 100 by 100 tile grid which has had a number of rooms connected with hallways carved out of it so if we zoom in we could see the tiles are little sprites and so we have these kind of rooms and corridors that you know we could have our player running around it and so this is the finished version I'm going to take you through a little tiny bit of setup just to set this up in the 2d roguelike project explain the idea of what we're doing and show you a little bit of how the algorithm works and then I'm going to start walking through the scripts so put that up for a second and let me change projects real quick to my empty one all right so here what we've got is a new 2d project and I've imported the 2d roguelike asset package from the asset store so in this project we have a folder called completed now it's not going to have this 2d generation folder I will post the scripts for you guys to grab with the archive but for now just just sit back and watch so in here we've got our four scripts that we're going to use what we're going to do is we're going to piggyback off of the completed scene in the completed scenes folder so we're just going to go completed scenes main and then there's actually a script in here that instantiates the game manager the finished game manager that's in prefabs I'm just going to keep it in the high our key just to make it a little easier to work with so I'm going to drop the game manager in and then I'm going to take off so if we just look at this is the way this game is as is right so it's a much smaller grid we can move around we can connect these pickups and as you can see the walls that are generated are just these kind of random shapes right they're not like rooms or corridors and so what we're going to do is we're going to change this so we have a much bigger grid with these more kind of room like level generation and so what I'm going to do is I'm going to take this game manager and I'm going to remove the existing board manager script so I'm just going to remove component and then I'm going to add my 2d generation board creator script and there's some default values here I'm going to start off using these the first thing I need to do is plug in my floor tiles so I'm going to use the prefabs in the completed folder and what I'm going to do is I'm going to lock the inspector so that I can multi-select and drag these in so I'm locking the inspector for the game manager grab shift click to grab flow one through floor eight and just drop those right on floor tiles Boop grab the walls drop those in there and then the outer walls as well don't forget to unlock the inspector once you're done I've done that a few times on the stream where we go don't know what it's not working okay so now we've got our board creator set up if we hit play and I'm actually going to let's disable the sound manager to do let's just pull the sound manager here I'm just going to turn off the music and stuff just so I don't have to talk over it just deactivating those audio sources on the prefab so it will instantiate notify all right so now when we play and pause we should see we zoom out yeah we can see that our level is being generated right so the thing that I'm going to do right now we have what looks like a sort of a moderately complex level layout with some interconnected rooms and stuff what I'm going to do is I'm going to massively simplify this so we can understand how the algorithm is is working and generating in rooms so I'm going to take I'm going to exit play mode take the game manager and we're just going to go down to I'm going to keep a high board size so 100 by 100 board size but I'm going to take the number of rooms so here we can choose randomly between how many rooms so a minimum of 15 rooms and a maximum of 20 and we can also set the range for the height so a minimum of 3 tiles wide and up to 10 tiles and a minimum of 3 tiles high and up to 10 tiles high and then these are also connected by corridors that have a minimum length of 6 and a maximum length of 10 and this is we're going to spend a lot of time talking about this so don't worry if it's not clear yet what I'm going to do is I'm just going to set the rooms to let's say 4 so minimum and maximum of 4 the width is going to be 4 so I'm just going to go fours all the way down so we're going to end up with a very we'll keep the corridor length the same we're going to end up with a very simple little layout play our scene pause and there we go so we can see what we're generating here is actually a linear chain of rooms right here we can see they're all the same size they're connected by corridors of various lengths but so basically what's happening here is in our script we're generating the first room and the first room is always going to be quite close to the middle of the board right so here you can see it's almost almost exactly in the middle of the board right and then we're going to generate a corridor going off of it and the corridor could be going either north south east or west right in this case it's going west so we have a corridor going west and then at the end of that corridor we're going to generate another room and we're going to repeat the process until we get up to our randomly chosen number of rooms right so here we lay out our first room lay a corridor generate another room lay another corridor this time going north generate another room and lay another corridor once you get up to the higher numbers of rooms let's go up to let's say 40 we get these kind of more complex interlocking patterns because there's nothing in the script that prevents the rooms from generating on top of each other and overlapping we do have some stuff to prevent it from just doubling back and forth on top of each itself again and again but it can go back to where there was a room and create a new one and that's why we get some things like this so here we can see we've got two rooms overlapping right and here the two corridors going out of there and I think that's pretty cool because it you know it gives a more varied look to the dungeon like here we've got like a number over lapping together and but again right you can tune the tune the setup to your taste and to the needs of your game right you might like having a linear chain or you might not and that's up to you and that's kind of the fun or some levels you might want to be more linear some you wouldn't and that's kind of the fun of procedural content generation is that you can you know kind of tune the algorithms and get it to spit out stuff that's cool for you as you can see we're getting a little error down here the game manager is trying to call a function to layout the board on our board manager so let's just get rid of that I'm going to open the game manager in monodevelop whoops and down here we were just calling setup scene on the board manager script which we have taken off that object so I'm just going to get rid of that and we should be error free you can also see that the player is being instantiate or actually is not instantiate the players in the scene and is just starting down here in the corner which is totally blocked in at the end of the session if we have time I'll just write a little piece to place him in one of the rooms just remind me to do that if I forget because that's not actually in the main scripts okay so with that said that is the kind of concept of what we're doing and so now let's take a look at some of these scripts so let's open up board creator and so here I'm just going to take you through I'll take you through the variables and the kind of top level overview of what the script is doing and then we're going to go look at the other classes the room and corridor classes that are kind of the meat and potatoes of the generation and then we will double back and and finish board creator so we're going to jump around a little bit I would say just focus on the concepts I'm going to make these scripts available for you guys to download don't try to copy them because they're pretty long I'm just going to talk through okay so the first thing that we have up here at the top in our board creator class is we have a public enum called tile type and within it we have wall and floor what we're doing here is we're going to build out a jagged array of the board and set within it each square to be either a wall or a floor as we do our board generation and then at the end we're going to loop through that array and actually instantiate it as tiles so we're not working directly with the tiles to generate the rooms we're going to do all of that inside an array and then once we've calculated it all we'll spit that array out as tiles and so we're just using this enum to store just tile type just saying this is a wall or this is the floor in the array which will then read to create tiles out of so here we've got a public integer called columns and a public integer called rows this is just the vertical and horizontal dimension of the board so right now our board is a hundred by a hundred tiles here we've got four variables of the type int range and this is another class in the project that I'll show you guys right now basically what this is is it's just a little helper class that we use to generate random numbers so let's just open that up real quick so here in int range we have a public integer called minimum and maximum and in the constructor we can pass in the minimum and maximum and you and then get and then access the a public integer called random which has a property saying any time we try to get this number return a random number between minimum and maximum right so and in this case because this is called random we need to specify here that we're calling unity engine dot random rent and so we're using random dot range right to give us a random number between minimum and maximum so you'll see this a bunch of times it's just so that we can avoid typing random dot range minimum maximum or with you know minimum with maximum width throughout our code so it's just a little thing to make things more concise so if we look at our board Creator here we've got four of those right so we have the number of rooms which is a new in train j-- that takes so in the default is between 15 and 20 rooms right the width is going to be a new int range that's between three and ten units wide height is going to be between three and ten units high the corridor length is going to be between six and ten units long then we have those arrays that we filled these are just arrays of game objects that store the prefabs we're going to instantiate between we're using arrays because we randomized which prefab like we have eight different floors and eight different walls strictly speaking you don't need to do that if you wanted to use one floor tile and one wall tile you could just make this a regular game object variable but it just makes it look a little more visually interesting here is the jagged array that I was talking about before so this is an array of tiles right and so or this is a rate of tile types right and so here we're going to set the length of the array along each axis right and so theoretically actually these could be different lengths in this case we're doing a 100 by 100 and this is called tiles and we'll see some more about how that works in a minute we also have an array of rooms which we're going to use we're going to fill it with our rooms as we're generating the rooms for the array right so we have this is an array of the class room right and we can open that up and so in this script we have all of the generation code for our room right so we say we want a new room and then within that room we're going to call setup room which will generate the room based on the width and the height and some other things that we'll look at in a minute we then have the same four corridors right we have an array of corridors that we're going to fill with corridors and generate corridors based on that so here is our corridor script and again this contains all the logic to generate one corridor right we have a starting position a length direction and so on and we'll go through that in detail then we have a game object called board holder this is just a transform let's just keep a little level up here so that we can see and actually let me just go for something much simpler so we can let's go back to our 4x4 R for room level okay well that one's not so good double back on itself already there we go alright cool so I'm going to zoom in on that so as we can see board holder just holds all of these tiles right we can see the grid of all the tiles board holder just keeps our hierarchy from looking like that right so we can see more easily so in start right we're going to instantiate the board holder object just make a new game object called board holder we're going to call setup tiles array which just creates our empty array right of tiles our tile type array then we're going to create the rooms and corridors right and this is really where the magic is for this session and we'll spend a bunch of time talking about this then we are going to set the tile values so we fill our rooms array and our corridors array with room and corridor classes that have different sizes and lengths and then we're going to go through and set the tiles in our tiles array to be either tile type floor or or tile type wall right so we do it for the rooms we do it for the corridors and then finally we instantiate the tiles so we take that grid of information about this is a floor this is a wall and we actually instantiate the prefabs and then we have some little helper scripts to do that just instantiate from array this just cutt uses the random has some code to choose a random prefab from those arrays and we'll go through that but so basically we're going to create the array calculate where the rooms and corridors are set the values in the array based on what we calculated for the rooms set the values for the corridors instantiate the tiles and then we have we instantiate the outer walls and the outer walls are just just this border here of outer wall it goes around the map one of the things that's unique about this is these tiles are actually destructible if you did the scavengers 2d roguelike project you'll know that the player can actually chop through these in three hits so this is kind of like a hedge maze more than a dungeon and so the outer wall tiles are not destructible right so the player can't leave the board but obviously you don't need to use these assets or any of that stuff you could just use this algorithm to make for whatever kind of game you want okay so let's take a look what I'm going to do is I'm going to walk through create rooms and corridors and then we'll refer to the room and the corridor classes as we get to those functions that are called let me just look at the chat just to make sure everybody's doing okay so another thing that I should mention here guys is that although this is it called basic 2d generation basic 2d dungeon generation this does assume that you know a little bit of c-sharp basic stuff so I would probably classify the difficulty of this as more of a sort of intermediate level than a beginner level so you know I see some questions in there what's an array and stuff if you don't know what an array is at this point you're probably going to want to do a couple of our more basic tutorials before you try to tackle this I'm assuming you know that you do have a little bit of stuff under your belt before doing this because it is a little bit more you know a little more complex okay so we'll discover this quickly because it's easy so the first thing that we're doing is we're setting the tiles array to the correct width right and hopefully as you grab these you'll see we have pretty nice comments all throughout so you can follow along after the fact so we're saying tiles is a new tile type array of width columns right then we're going to go through the lengths of tiles and set each column to be another array of the length rows right so what we're doing is we're starting out down here in the corner right and we're starting with this we're saying okay let's set this whole thing to be a hundred long and now we're going to start on this one and fill it in vertically along the y-axis to be a hundred high and then one hundred high and then one hundred high that's what's happening with these nested for-loops and when you're laying out grids and working with grids and stuff this is a fairly common type of setup to see okay and the good thing about somebody was asking why use a jagged array well if you wanted to have like a 50 by 40 board or or 27 by 46 you could because the you could set the lengths independently alright so the next thing that we're going to do right calling from so we set we call that then we call create rooms and corridors right and this is where the generation is really happening so the first thing that we're going to do is we're going to say our array rooms is going to be equal to a new array of rooms right of our room class using num rooms dot random right and so you remember num rooms is an it range that already has its minimum and maximum set in this case to 15 and 20 right so if we say num rooms not random it just means give me an integer between 15 and 20 right so the number of rooms based on the the default values here is going to be we're going to get an array of rooms that's between 15 and 20 rooms long right then we're going to do the same thing for corridors but the corridors are going to be a new to our array of corridors is equal to a newer array of the type corridor but we're going to use for the length of the corridors array the length of rooms so rooms dot length minus 1 and this is because we don't want to have corridors to nowhere right you can see here we made four rooms but we don't have like a corridor just sticking off you're going nowhere because we have four rooms and three corridors right so we use one less than the number of rooms for the corridors then we're going to create the first room and corridor now the first time we make a room is going to be a little bit unique because it's not connected to anything we're just picking a spot in the middle of the board laying the first room and then letting the first corridor off of that so you'll see how it differs from the kind of main loop of creating the rooms and corridors as we go through so what we're going to say is the first index in our rooms array right rooms 0 is equal to a new room the first index in our corridors array is equal to a new corridor right then we're going to call routes so we added a room to the array now on that room that we added we're going to call setup room right now when we call setup room we're passing in some parameters right and so let's look at the function signature for setup room now here we are in the room class right and it's worth noting that we have set up room and then we have an overload for setup room the only difference between these two is that the overload takes a corridor whereas the first implementation doesn't right so the first time we call this there's no corridor going into the room so we can't pass in a corridor as a parameter which we will do later right so this is only going to be used for the first room as it says here in the comment right so we're going to pass in it's going to expect right when we call it an int range right which is going to be the width an it range which is going to be the height range and then integers for columns and rows which are just coming from the board creator saying how big is the board that we're generating this room in we're going to use that later to make sure that the rooms don't go outside the boundary of the board right and you'll see that a lot of the code that we're going to be doing here is going to be to do that right because we're moving randomly we don't want corridors or rooms that are going to extend beyond the space of the board so this is actually really pretty simple we're just going to say the room width of this room is going to be equal to width range dot random right so a random integer coming from width range which was passed in when we created it right so board creator room width here so in this case it's going to be between 3 and 10 right so now we're going to we're going to let's move that other way now we're going to actually I can close this for now we don't need to look at that again so we're going to pick a number we're going to say okay room width is equal to 5 right the height is equal to 6 let's say then we're going to set the x position and Y position of the room right and the x position in the Y position is up here is a public integer variable that's the x coordinate of the lower-left tile of the room and the y coordinate of the lower-left tile of the room right so if we look at our map let's look at this is going to be our first room right because it's closest to the center so the rooms are always calculated from this tile right this is the position that this x and y coordinate in this case 4848 is the origin for the room right and then we're going to build the room out based on that right so that's what's going to be stored so for this first room it would be the x position would be 48 and the y position would be 48 and we're calculating that by saying take the number of columns in the board in the whole board and then we're going to take the room width subtract so - f - so 2 - the room width divided by 2 so all this is doing the room width is a random number right so this is just creating and we're doing the same for the for the Y position this is just create a little bit of random movement around the center so it's not always just starting dead center but basically we're just putting the position of the room is going to be is always going to be roughly in the middle so that we have some space to move outward in this case it decided to head east to start right and so that's all we're doing for our first room now notice the next implementation of setup room is much longer because we now need to kind of worry about the edges and make sure that we're not going out of the edges in the first room we know unless it was a room that was a hundred by a hundred we know it's not going to go outside of the bounds so it's very simple right so if we jump back to board creator we're calling set-up room right passing in the width and the height and the size of the board and next we're going to generate a corridor leaving that first room right so we're going to take the first index in our corridors array that we created and we're going to call set up corridor on that corridor now notice the first parameter that we're going to pass in is our room right so we created a room and we're going to pass it to the corridor so the corridor knows the rooms position and then we're going to pass in our in train jh4 corridor length so in this case it's going to be between six and ten right then we're going to pass in the room width and the room height so the range right the in train just for room width and room height the size of the board and columns and rows and in this case the boolean true which is telling the corridor yes you are the first corridor so if we look at setup corridor we can see it takes a room it takes an int range for its length an int range for the possible room width and room height the size of the board and columns and rows and then this boolean to check if it's the first corridor so instead of using an override over here we're just using a boolean to say is this the first corridor yes or no so in the first case it's going to be true so if we just look at our corridor class right we have up here at the top we have an enum called direction right and this contains north east south and west so these are all the possible directions that the corridor can go in right and so next we have a public int called starting exposition right this is the x-coordinate for the start of the corridor starting Y position which is the y-coordinate for the start of the corridor the corridor length how long is it going to be and then again the direction right so we also have these two integers and position X and end position Y which are going to calculate the end position based on the starting position direction I'm going to come back to this I'm going to come back to this later when we actually use it so let's look at setup corridor because we're setting up our our first corridor so the first thing that we do is we say okay Direction is equal to a random number between 0 and 3 right excluding the maximum which is 4 so a random integer between 0 and 3 which we're going to cast to our enum Direction right so it's worth noting with enums you can flip back and forth between an enum and an integer and we're actually going to use that aspect of enums quite a bit in this script so we're going to say ok our direction is equal to a random integer which we're going to turn into a direction so one of north east south or west right now this is a little bit tricky you can see it has a long comment in front of it basically what we want to make sure if we look at our map right let's say we start here and then our first corridor in this case is going east right we spawn a room here and we want to spawn another corridor we don't want it to go back west immediately right because what could happen is we go east then we go west then we go east again then we go west again and we end up with a dungeon that's just like covers this part of the board right there may be cases where you would want to allow this in your generation and that's part of the kind of artistic aspect of deciding how you want to implement this but in this case we wanted to make sure that it was going to keep going and covering spreading out covering more territory instead of doubling back on itself so what we're doing is we're calculating the opposite direction of the direction that we came from right so we're going to say okay we have a new direction a new variable of the type direction called opposite direction and that's going to be equal to the room entering corridor direction right so remember we took a room as we called the function and so we're going to say okay room has this direction public direction variable entering corridor which stores the direction of the corridor that came into this room right so if we look here so here's our first room so when we're generating this room or the corridor leaving this room right the entering corridor was coming from the east right so we're going to say okay let's say if the entering corridor was East we're going to add two to it so we're going to cast it to an int right and then we're going to add two so what happens to our enum if we start with east and we add 2 we get west right and this is an important aspect of the way this you know so normally people would say north south east west right we're set up north east south west so that we have this relationship between our directions so if we add two we invert the direction and if we add one we're going to turn perpendicular to the direction we were right so here we're adding two to get the opposite direction right and then we're using the modulo operator here the remainder right to say okay if we on west and we added to we would end up with five right which is not part of our enum so instead by saying okay we get five but then we divide by four and take the remainder we get one which is east right because this is zero one two three so this this kind of construction of using modulo can be useful when you have either arrays or enums or lists where you might want to do some mathematical operation but you don't want to exceed the length of your of your whatever you're working with so then we're going to cast that back to a direction right and store it in opposite direction then we're going to check if this is not the first corridor and the direction is opposite direction so we've got the situation where we've generated a corridor that is trying to double back what we're going to do is we're going to take the we're going to declare a new integer called direction int and set it to equal our direction right which was the random number that we created up here the random direction that we created as an INT and then we're going to add one to it right so if we had east like we do in the example and we're trying to go west now we add one and we get north right so we're going to turn perpendicular to the direction we were going by adding one and then again we're just going to say okay direction in equals direction in modulo four right meaning to make sure we don't get a five or a six or I think those do it well now a 4 would be the only possibility right because we're just adding one make sure it doesn't overflow divided by forming at 0 back to north and then we're going to say Direction equals direction int right so basically we're just saying make sure that if we did generate a direction going back on itself we're going to instead we're going to turn okay then we're going to generate the corridor length right so the corridor length is going to be equal to length random this is coming from corridor lengths right which is getting passed in to set up corridor right here it is and so basically we're just saying okay this is going to be a random integer between 6 and 10 and now we're going to do all of this stuff just to make sure that our corridor does not go off the board right so if you didn't care about going off the board you could ignore all of this right you could just generate it purely randomly wouldn't matter right but in this case we want to make sure our corridor doesn't go off the board and so what we're going to do is we're going to say okay we're going to create an integer we're going to declare an integer called max length and that is going to be equal to the max property of our length int range right so the max length cannot be longer than the maximum possible length specified here right so in this case it's 10 so the max length is going to be equal to 10 and then based on our direction we're going to modify its possible positions right so let's just go over north so if the direction is north the starting position so can be random but within the range of the room right so let's look at this this one is going north right so we could choose from if we're going north we could choose from any of these along the x axis right we don't mind which and actually it's kind of cool if it's in a different place right as opposed to just being from the middle or always being from the edge right it could be any of these four but the starting Y position is going to be the Y position of the room plus the room height right meaning it always has to be in this row right so we're going to here would be the the starting y position is here right we're going to add the room height to get to this to get to this line of spaces right so we always want it to start from the top of the room if we're going north we're going east we always wanted to start from the east side if we're going west we want to start from the west side if we're going south we want to start from any space on the south side right then we're going to say the max length of the corridor is going to be equal to rows so the total size of the board minus the starting Y position minus the minimum room height right so as we can see in the comment here the maximum length the court can be as the height of the board but from the top of the room right so we just want to make sure that this is not going to go off the room go off the board and so we're checking where is it starting and making sure it's not going to exceed the length of the board now at the point that we're at now right we would need a forty unit long corridor but once our thing wanders over to the edge this does actually become relevant and so we need to control for it right we're doing almost exactly the same thing in each of the other directions but in this case right the x position if we're going east is fixed right it's always going to be on this side and the Y position is going to be the one that could be randomized because it could be one of these four squares as long as it's on the east side right then finally we're just going to use math F clamp right which clamps a number between two values and so we're going to say corridor length is what we want a clamp it can't be shorter than one and it can't be longer than max length so just making sure that our random number that we generate doesn't end up bigger than we possibly want it to based on the calculations in this switch so so that's our corridor right so what we're doing here is we are generating that first corridor and telling it yes this is the first corridor so we're going to in fact right we'll skip over we'll skip over this it is the first corridor here right so we're not going to do any of this but I just go through all that so that we were done with the corridors so that now when we move on to the next part of our function here so once we've set up the rooms and corridors now we're going to run a loop over the length of room stat length so if we have if rooms that length is ten rooms right we chose to create ten rooms we're going to run through this loop ten times and notice we're initializing the loop to 1 because corridors 0 has already been set right rooms 0 and corridor 0 is already set because we set it up uniquely for the first room the so now we're going to say ok rooms 1 right since we load will kind of walk through the first iteration of the loop and then the rest are going to run the same way so rooms 1 is a new room right so we're adding a new room to the array and then we're going to call set up room on rooms 1 passing in room width our in range room with our in range room height columns and rows for the size of the board and now we're going to pass in a corridor and we're going to pass in corridors I minus 1 so right now I is 0 I'm sorry I is 1 right we initialized I to 1 so we're going to pass in corridor 0 which is that first corridor that we create it so the we're always going to be working from the lot the new room is going to take the direction of the corridor that we just created in mind right because it's going to use to generate the room so let's take a look at our more complex setup room overload here which takes as we can see a parameter of the type corridor right the rest is the same we have the width range height range columns and rows but it also takes a corridor because now all the rooms that we generate are going to be coming off a corridor okay so the first thing that we do is we say entering corridor right the direction for our room is going to be equal to court the direction of the corridor that we passed in right so corridor direction so in our example here here's our first room now here's our second room so the entering corridor was going east so entering corridor is going to be equal to East then we're going to set random values for the width and the height right so the width range is going to be random and the height range so we're using the in trains again to get random numbers for each of those and and remember I'm using 4 by 4 but obviously you could have 2 by 10 you know you can have a wider to make rectangles and long skinny rooms and stuff depending on the values you put in I'm just doing squares easy to look at then we're going to do this guy and this is very similar actually to what we did in corridor where we have a switch that takes the direction right so and I kind of skipped over what a switch is a switch just says it's like a bunch of if statements but it's just a little cleaner than that so here we just say ok the corridor Direction is what we're checking if the corridor direction is north do this stuff if the corridor direction is east do this stuff if the corridor direction is south and west etc right so it's just a little bit of if you need to if you find yourself writing like 5 if statements you might do better with a switch right I remember when I learned that I was like oh my god life life was better ok so if the corridor entering this room is going north we're going to say the room height so we want to make sure we're going up right we're going north we want to make sure that the room is not bigger than the size of the board right so the room height is going to be equal to math F clamp so the value we're clamping is room height can't be less than one but it can't be more than rows - corridor dot and position Y right so here's where we're using that end position Y so let's jump back to our corridor for a second to see what that's doing right so basically we're just making sure that we're subtracting the end position from the total number of spaces in the board to make sure that the height cannot be greater than the size of the board right so in corridor these are just integers right but we're using a property so whenever we get them it's going to do some calculation so here let's we'll look at in position Y since that's what we were looking at so if the direction is east or west the end position is going to be the same as the start position right because the corridor is moving like here it's moving vertically the x-coordinate never changes right so we're just going to return the start position right so the this I'm sorry the the x position changes the Y position never changes I misspoke so the the Y position never changes so we're just going to return the start position is going to be the y coordinate is going to be the same if the direction is north we're going to return the starting Y position plus the corridor length and then we're just subtracting one to get this guy the length the last tile in our corridor and then we're going to create the room at the end of it right finally if it's neither East nor West nor north right so all of these if statements pass through then it must be south and so then we're just going to subtract along the y-axis right so we're going to take the Y position subtract the corridor length and we're going to 1 right so basically we're just going to count down and then add 1/2 to get us back here ok and then we're doing the same thing on the X right we're checking if now instead of checking if we're going east or west we're checking if we're going north and south and we're going to return the same X position otherwise we're going to check if we're going east and then we're going to add the corridor length right going this way and then otherwise if we're going west we're going to subtract going this way right ok so here we're using that to check and make sure that we're clamping the height of the room to be less than the number of rows on the board minus the end position of the corridor so the y coordinate of the room must be at the end of the corridor since the corridor leads to the bottom of the room so the Y position of the room is going to be equal to corridor and position Y and then the x coordinate can be random but not outside the width right so the x position is equal to random range corridor M position X minus room width plus 1 and so that's the minimum value right and then the maximum value is going to be corridor and position X right so we're just basically going to generate we're going to place let's say here right we're going to place this tile which is the origin position for the room based on because we're moving north we can randomize it right and so we can kind of wiggle it back and forth a little bit but not not obviously more than the width of the room because it would not connect right and then finally we are going to clamp it to make sure it doesn't go off the board right so again we're using X position x position equals math F clamp the value we're clamping is X position it can't be less than 0 and it can't be greater n columns - room width rub rub rub yeah and then we're doing the same thing along the other directions right so in the case of East we are saying instead of checking the height we're check we're instead of setting the height we're setting the width right and we're doing the same thing but for the exposition basically just rotating what we were doing but using basically the same structure so and that is actually as far as the actual procedural generation stuff that's basically it now once we get over here there's kind of a bunch more housekeeping stuff that we have to do to actually lay it all out but in terms of generating the shapes that's that's done it so and as you can see right most of the time we're spending is just trying to make sure stuff doesn't go outside the board so it's it's it's not crazy complex it's just you just need to make sure that you don't just generate a bunch of garbage which is always the risk with with random generation right you got to shape that randomness and control it and kind of keep it in a bottle okay let me look at the chat Oscar Russo asked also how do you ensure you have no overlapping rooms in this we are not checking for that we have overlapping rooms in this and I'm actually calling that a feature not a bug because that allows us to have more interconnected dungeons that look a little cooler by overlapping and also like irregular shaped rooms if you didn't want that you would probably want to use a different a different approach yeah so that's kind of like the fun part right and then so basically we've called setup room right which I just walked through and then we're just going to loop through corridors lengths or we're just going to check if I is still less than corridors that length will make a new corridor and call setup corridor on it right which we which we went through so as long as corridors dot length right remember corridors that length is 1 less than M of rooms we'll make a new corridor once we run out of corridors we'll stop making corridors it's pretty sensible right okay so now we're just going to set the tile values for the rooms right so now we're going to set the values so right now all of this stuff is just living in our rooms array and our corridors are right right it's not actually on the grid yet it's just a bunch of numbers in these two arrays now we're going to actually spit that out into our jagged array tiles right so now we're going to do a for loop over the length of rooms right so we're going to loop through the rooms array that we created to get all the rooms and we're going to say we're going to declare a new room called current room and that's equal to whatever room we're at in the rooms array and then we're going to run a for loop on that and go through its width right so we're going to say int J equals 0 J is less than the current room's room width right then then we're going to iterate right so we're going to say the x-coordinate is equal to the current room's x position plus J right and then we're going to go so we're going to we're going to go basically one at a time right so let's say we take this room we're going to have first of all we're going to set the Derb if I can click on it we're going to set the x position and then we're going to loop up along the y-axis and set the Y position of each of these before we move to the next x position right and that's what these nested the loop inside the loop are doing so then we have another loop here that goes along as long as we're less than the room height that goes along the Y coordinates and sets Y position plus K finally takes both of those values right it takes both of these the x-coordinate and y-coordinate and sticks them into our tiles array and sets that to be a full right so it's looping through all of the rooms and setting their tile type remember this enum up here right it can be either a wall or a floor and so it's looping through all of those all of the rooms and setting the tiles within the bounds of each room to be a floor right and if you're not used to this structure of these nested for-loops to kind of loop over a grid it can be a little counterintuitive but you can just imagine that we're just going one column at a time and going up to the top before we move to the next column right so we just say take this one fill all these take this one fill all these and so on okay so we now we've recorded the we've set floors for all of our rooms now we're going to do for our corridors right and we're doing very similar thing now we're going to loop through the corridors length we're going to say the current corridor is whatever wherever we are in the loop and then we're going to go through its length so another nested for loop right we're going to say the current cord so as long as J is less than the current corridors corridor length so the we're going to say the start coordinate in the tiles array well we're declaring a new integer called x-coordinate right which is what we're going to stick into the array eventually is equal to current corridor dot start X position and the Y is equal to start Y position then depending on the direction we're going to either add or subtract from that right so let's say we're going north we're going to take the y-coordinate we're only going to modify the y-coordinate right because the X can stay the same and we're going to say y-coordinate is plus equal to j which is wherever we are on the loop right so and then we're going to do for east we would add to the x-coordinate for south we would subtract from the y-coordinate and for west we would subtract from the x-coordinate right finally when we end up with those numbers we stick them into the array again so tiles x-coordinate and y-coordinate are equal to floor right so now what we've done is we've set in our array all of the rooms room shapes to be floor and now all of the corridor shapes to be floor right we haven't created any tiles yet this is all just inside our inside this private array of enum okay then now right in start sorry a lot of zipping around there we're going to call instantiate tiles right now we actually are going to turn them into prefabs so now we're going to call we're going to call instantiate tiles right and we're going to say okay again same nested for loop structure first we're going to loop along the X so for the first exposition we are going to so here we're just going tiles dot length which is the length along the X right or the first dimension of our array and then in J equals 0 for as long as J is less than the length of our second coordinate at I right so again we're doing the we're going to go through all of these we're going to start at 0 right and work our way all the way to the top and then we're going to go to 1 and go up right so we're doing this kind of a educational mouse waving that's what I what I do for a living so wave the mouse around it's going up up up okay so the that's good right are you not educated so we're going to loop along and then we're going to call this instantiate from array function and on instantiating from our floor tiles array I'll talk about instantiate from array in a minute but it's simple and basically it's just picking a random prefab from our floor tiles array here just picking one of these and instantiating it now and that's just so that we can have if you look at the floor tiles right you can see they're all different and they look Brown and pretty thanks to our artist Pete Lee at PDR strike on Twitter excellent art maker okay so the now the reason that we're placing everything gets a floor and that is because we can destroy the walls and then we need a floor to be underneath it right so if you didn't have that capacity to destroy the walls you could just say if the tile type is floor put a floor otherwise put a wall in this case we know we need a full grid of floors but we're going to check if the tile type is wall we'll put a wall as well on top of that floor right and obviously for the ones that we've tagged floor during this process it won't put a wall there and then we will end up with our actual board and then we're going to call instantiate outer walls which is super simple and just lays out basically just goes rip rub rub rub rub rub rub up that way it makes that sound well it does it and then down the side and lays out the outer walls right and so here it's just a we have left edge right edge bottom edge top edge and then we're just calling instantiate vertical out of wall which just loops from the starting Y and then goes up up up up calling instantiate from array along the Y right so just moving up making tiles do the same thing for horizontal and then instantiate from array is just takes a array of game objects as an argument right call prefabs and then the x-coordinate and y-coordinate to instantiate at it generates a random number within the length of the prefabs array creates a vector3 called position using x coordinate and y coordinate and then instantiates it right so makes tile instance equals instantiate prefabs using that random index right so a random number inside that array using the position that we just created quaternion identity just means no rotation as game object and then we set the tiles parent to the board holder right just so that we can do that and that my friends is how levels are made at least weird 2d dungeon hedge maze levels in this part of the universe but hopefully that's been helpful for you for those who are new thank you for joining us for the regulars thank you for coming back and I am going to hang out in the chat and answer further questions related to this please I'm not going to be fielding other questions because we got 250 people in the chat and we got to keep it on topic tonight and thanks to Adam Buckner and my guide for joining us and thanks to you for watching Jeff Fred Luigi asks can you answer my question what would be a good way of spawning monsters treasures game objects in the rooms well what I would do probably is add that to the room generation code so we would add to our enum you know a monster prefab add an array of monsters and then just as we're generating the room have it give it a chance to stick a monster in there along with the treasure and whatever else not too hard I don't think Kuno knew only thank you I'm glad it was engaging and motivating that's what we hope to do here all right yeah I mean I think basically right once you've got your I would probably just make the monster generation a part of your room generation right or at least call you could have a monster generator script right and just call that let's say you could just choose X number of tiles in the room and instead of turning them into a floor just instantiate a monster there oh yes okay so thank you Coonan Oni there was something I was not I was supposed to not forget to do which was to add to this the ability to spawn the player right so you guys can actually test this and walk around so okay so what we're going to do is we're going to jump into the board creator and we're going to add a public game object player Boop and we're going to make the player into a prefab so I'm just going to grab the player from the scene drop them into the prefabs folder to make a pull up oh we had one already okay actually there's one thing I need to do let's delete that let's take this guy well I could go messy okay we need to child the main camera to the player the main camera in the original project has this loader which enforces the singleton pattern for the game manager what I'm going to do is I'm just going to make a new game object called loader and just add that loader to it instead so and then we'll put the game manager in the sound manager in there this just checks if we have a game manager and a sound manager and adds them if we don't and that means we can now take it off the main camera so I'm going to remove component loader on the main camera I'm going to child the main camera to the player right so that the camera will move with the player and it should be good we just and then make sure it's not offset right let's Center it on the player whoa oh not don't change the z-depth should be negative 10 right and then whoa did so player get broken so you still there hey oh it's the animation that's why oh and I'm doing this all in pause mode that's dumb gosh I always do that to put my play mode tint on every time I upgrade the editor I forget to do that okay anyway this will be fine we'll call this player Explorer this is all stuff that's just specific to this project you know this implementation in this project so you don't strictly speaking need to do this but that's kind of fun so okay then on the game manager we're going to add the player Explorer to the player field that we created and will delete the player from the hierarchy I'm going to save this as let's save the scene as oh I can't because I'm in play mode uh okay delete the player son I lost my loader object derp okay that's what I get for trying to do things fast and this will be mein revised okay and then just make sure yeah we got to put this on there or put the player Explorer in there save and in the script it's pretty simple all we're going to do is when we're creating the rooms I'm just going to say in this for loop as we're looping through the the rooms I'm just going to add a little quick check to say if rooms I equals I know sorry not if rooms I if I'm losing my mind as this long hours of talking so if I equals rooms length 2 times 0.5 so basically to get as we're about halfway through we're just going to say instantiate player at I'm trying to remember I should have wrote this down rooms I dot X position we got to make a position right hold on vector three player position equals new vector3 so you got something got some live coding after all rooms i dot exposition rooms i dot y position Y position zero so we're going to instantiate player app player position and quaternion dot identity okay it's enough hopefully that works the first time let's find out that set game manager is there this should be fine yay oh this to audio listeners in the scene I didn't delete the camera let's delete the camera get you out of here let's make sure it works okay well I've got some error but anyway as you can see the player is now instantiated Midway roughly in our chain right you could have them in Stan she ate in the first room or the last room if you want to have them start at the end because our dungeon generator kind of treant trends towards making a linear chain I figured putting them in the middle then he has to choose which way to go to start might be more fun whatare am I getting some oh oh yeah it's missing a um a reference to something I remember I have to fix it before oh yeah the food text you just have to find a reference to the food text just do like game object up find or do it some other way but anyway that's the relevant part here is just instantiate half way through the loop or at the end of the loop or the beginning of the loop but just drop the player in there and then he'll end up in a room all right cool yeah Adam pasted the rogue Basin link that's a great place to find more about this stuff cool all right guys so what I'm going to do is I am going to I'm going to cut my mic off and cut the stream but I will hang out in the chat for a little while longer and continue to answer some questions if you have them and yeah thanks for watching
Info
Channel: Unity
Views: 114,258
Rating: undefined out of 5
Keywords: Unity (Software), unity, roguelike, tutorial, procedural generation, random level, 2d level, Roguelike (Video Game Genre), Video Game (Industry), Dungeon Crawl (Video Game Genre)
Id: wnoLaui3uO4
Channel Id: undefined
Length: 71min 36sec (4296 seconds)
Published: Tue Oct 06 2015
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.