Create a grid in Unity - Perfect for tactics or turn-based games! Part 2
Add functionality to the grid we made last video. Randomize tiles, spawning units, game flow and taking turns.
This is a long video... I originally aimed for it to be nice and quick, but ended up being an unadulterated glimpse into the mind of a game dev. So enjoy!
Perfect for any tactics or turn-based game.
Take a shot for every time I mess up BaseUnit!
Part 1: https://youtu.be/kkAjpQAM-jE
Project Files: https://bit.ly/2TXh4Na
this is part two of the grid game series if you didn't watch part one go watch that now and then come back here to continue hey bros so last time i left you with this checkerboard grid this time i'm gonna run through a whole bunch of stuff like giving a bit of variance to the map spawning some units moving them around attacking and a few other tips and tricks along the way so unless i want this video to be three hours long which i don't i'm just going to absolutely smash through it okay so if you if you need any help with it just get into my discord and i'll help you out with it first let's organize our tiles so right now we just made this base tile but what if we want a whole bunch of different logic for example i might want a mountain or a stream or some lava and then i might only want this checkerboard pattern on my grass so let's start organizing that let's create a new folder called tiles and let's put our base tile in there then let's make two unique tiles let's call this a grass tile and then let's also make a mountain tile and open up the grass tile and this will actually derive from our base tile okay so make sure you use this one and not the unity tile maps one or you'll be in a world of hurt and then also the mountain tile let's derive from the base tile cool and then let's organize our prefabs here so let's make a prefab folder prefabs and then in here let's make a tiles folder and put that in there so that's now our base tile and on our base tile let's remove the actual tile script okay so we're never going to use this directly we're going to make variants of it so let's right click create prefab variant and let's call this a grass tile and then on this we'll add our grass tile script like that and then fill in our references and then just to make sure that everything is working on our grid manager let's put in our new grass tile in there and then press play cool so it still works uh let's make another variant for our mountain tile mountain tile and the only thing we need to do here is maybe just make the tile like color like that cool beans sorry in our grid managerial instead of just spawning this tile prefab let's rename this to grass tile and then let's also make a mountain tile like that and here instead of spawning it all the time let's just randomly select to spawn a mountain sometimes so in this section here this is where you would actually create be creating your biomes uh or your map variants so i'm just going to be randomly selecting a tile but you might have like tile weights so for example if you spawn maybe a stone tile you might have a weight to it so that maybe on the next hole uh there might be a certain amount of chance that that will also be a stone tile um or like really complex biome logic but for now let's just do a random tile so random tile equals and let's use the random class from unity engine range and we'll do min 0 max 6 and if that number is equal to 3 let's set this to the mountain tile otherwise let's set it to the grass tile and then instead of spawning that tile prefab all the time let's actually make it the random tile cool so now that should actually be enough to just spawn those two as it is so let's go to our grid manager and put in our grass tile and before we put the mountain tile on it actually needs the mountain tile script so let's do that and aha so we don't want to put the checkerboard on the mountain tile do we so let's actually make this a checkerboard logic specifically on the grass tile so the two colors let's remove that and put it on the grass tile let's actually remove those as we don't need them next this init function i still want to call this a nip function on every single tile but i don't want it to do the same thing on every tile for example so let's actually make this an abstract class and then this method can be a virtual method so basically now this logic will run on all the tiles but each individual tile has the chance to override this function and then do specific logic for them okay so and i'll show you what i mean uh but also we're sending in the offset here into all the tiles and we really only need the offset in the grass tile so instead of accepting an offset let's accept a into x and an into y just their coordinate okay and then on the grid manager here we'll we'll just send in the x and the y and so on the tile and now this here is the checkerboard pattern logic so let's remove that and here let's override the init function and then put that there and i know i'm going through this really rapid fire so if you can't keep up just download the project and come in and ask me questions it's fine so this needs the offset logic doesn't it so let's go to our grid manager and here let's grab this and put this here and while we're here let's just refactor this a subscriber thorough bowman on youtube said that i can do this in a far simpler way and he is absolutely right we can do x plus y modulo two equals one and that will do the exact same thing and then we need the renderer on our grass tile but it would be a mistake removing this and putting it on the grass tile because we could have a whole load of derived derived tiles that need this renderer right so instead of removing it let's just change it from private to protected and if you don't know what protect it is it's effectively private but your derived tiles can also access it okay so it's a good way to allow your derived tiles to access it without polluting the namespace cool beans so on our grass tile we are doing our checkerboard pattern and on our mountain tile we are not overwriting it at all okay and when you don't overwrite it at all that means just the initial logic on the base tile will run so let's try that out let's go back and on our grass tile on our grass tile we've got that there and on our mountain tile we just need to put in our variables that then in our grid manager we can now put our mountain tile and let's give that a crack there we go got a little bit of variance in our map now and as you can see only the checkerboard is on the grass and next you're probably wondering what this is and let me show you it is in fact my subscriber ratio and as you can see that's not too good rose subscribe all i ask is that you are deeply integrating me into your personal lives that's all i want all right i want to make you happy and i want to make you a better game dev so subscribe so you never miss a video and now that that's done i can delete that cool let's move on okay so now in our grid manager we're just generating this grid as soon as we get out the gate all right there's there's not really much organization going on here so i'm going to add a game manager to my project and if you don't know what a game manager is i'm going to link a video will be up that side of the screen somewhere go watch it it is a fundamental part of game design and knowing it will organize your game scripts significantly so go watch that come back and i will have this game manager uh up and running okay so i've got my game manager up uh we've got our game state a num here which can be generate grid spawn heroes spawn enemies heroes turn enemies turn and then we've got a change state function which will then put in logic here depending on the state so the first one is generate grid so in our grid manager let's make this a public static let's make this a singleton like that and in our awake function assign that and you should know what that is if you just watch the game manager video and now we don't really want to do this uh impromptu like that we will call this from the game manager so on our game manager generate grid and we'll do grid manager instance generate grid like that and as you can see in my start function i'm getting the ball rolling by calling the first state generate grid in our generate grid function when it's all well and done we will say game manager instance update game state and i'll change change state and the next state will be spawn players spawn heroes cool so it's going to come in generate the grid and then change the state to spawn heroes and now we're in spawn heroes the best thing to do would now to be create a unit manager so let's do that and we're getting a lot of managers here so let's create a managers folder and call this a units manager [Music] and then i'll move my other manager scripts in just to make it nice and organized all right let's make the unit manager delete that as i won't use it and i swear the amount of times that i make singletons i should really just have like a hotkey that scaffolds it out because i spent half my life making uh singletons all right so unit manager so now we actually need some units to spawn don't we and whenever i'm handling units i always create a scriptable object to hold information about the unit okay so let's create a new folder here this one will be called units and in here we'll have two subfolders heroes and enemies and then in the base folder here let's create a scriptable unit and let's open that up and this will be a type scriptable object and then we'll have a create asset menu and the file name will be uh new units and the menu name will be scriptable units so this is just a way to right click in our hierarchy in our project pane and create this new unit and now what does this unit need what information does it need to hold and just remember this is not the actual unit but just like an encasing linking all this information together so first let's create an e num called fraction and you can either be a hero which will be zero or an enemy which is one and then on here this will be faction and then we need a prefab to the actual hero unit that we're going to spawn so let's do that in our units uh let's create a base unit and then let's create in heroes let's create a base uh sorry in enemies let's create a base enemy and then in heroes we'll create a base hero and then let's create the actual heroes so i'm going to call this hero1 and then i'm going to create an enemy here called enemy one okay so there's going to be a hierarchy here so if we go into our base hero this is going to derive from unit base sorry base unit and then if we go into my hero 1 that is going to derive from hero bass bass hero so bass hero and then if we go into our enemies hero enemy 1 that's going to derive from base enemy and then as you guessed it base enemy is going to derive from base unit does that make sense so we've got this hierarchy here so at the bottom we've got unit base so we know that every unit on the field is going to need some shared logic for example setting themselves to the tile or taking damage all right and then we know that the base hero is going to need logic like selecting the hero and then actually doing the moves like moving the character or presenting their uh attack pattern or whatever and then the bass here the bass enemy is also going to need their own logic that's not shared with the other ones that might be like the ai and the logic right there might be ai for moving and attacking so this is a good little hierarchy to keep everything really organized so now that we've got our scripts let's create a new folder here in prefabs called units and then in here as you guessed it there's gonna be heroes and then enemies okay so so now we've got our scriptable unit which is like our container and then we've actually got our scripts for the actual prefab so let's create our first scriptable unit in assets create a new folder called resources [Music] now this is a special unity folder and i'll show you why in a second then create a new folder in there called units and as you guessed it i'm going to create more folders for heroes and enemies just to keep things really organized so in heroes let's create our first create scriptable unit all right so that's that asset menu that we made right here let's call this hero 1 now here we've got our faction and let's actually duplicate that here for enemy one okay so that's not enough info what we actually need is the uh the unit prefab now this will be a public unit base and i keep it on that the wrong way unit base unit uh unit prefab okay so here we're saying that it just needs a type base unit but ultimately we're actually going to be assigning uh specific heroes and units to it uh and enemies to it okay so let's actually create this unit so i'm going to create a script uh sprites folder and i'm just going to import my tarot dev mascots which i use in pretty much every video so let's actually make our first hero so i'm going to use my snorlax and it doesn't really need much right now i'm just going to have a sprite renderer and then i'm going to actually have my hero 1 script on there and then i let's create a prefab of that so my snorlax and then harley quinn can be my first enemy and i'll make that an enemy one and make her a prefab in enemies so now in our scriptable objects we can go to our hero 1 and attach our snorlax and our enemy one attach up quinn and make sure on your enemy you change the faction to enemy now i'll show you why the resources folder is important so let's create a private list of scriptable oh my gosh of scriptable units and we'll call this units now in our awake function let's set this to units equals and we'll call resources load all and we'll use the generic function here and we'll say it's of type scriptable units and then to look in the units i think that's what i called the folder units uh folder and we'll just say two list okay so this will go into our resources folder and look for the units folder and then look through all the subfolders for any type of scriptable unit and it will put it into this list here so now we can continue the flow in our game manager this is our next step spawn heroes so in us in our unit manager let's have a function here called public void spawn heroes okay now in your game you probably had a menu or something to select the heroes that you actually want to spawn and then once you actually get into the game you'll read the data from your save and then actually spawn those specific heroes but for me um in this tutorial i'm just going to say there's going to be one a hero count of one and i'm gonna loop through the amount of heroes that i need okay so i've only got one here so i'm just gonna do one and i'm just gonna randomly spawn it okay so i need to grab a random hero from this list so let's create a private and this is going to be a generic function get random unit and this is going to take in a faction okay so is it going to be a hero or is it going to be a enemy okay and this is a generic function so let's say that it is going to be of type t where t is base unit okay so now here i'm going to do a little bit of lambda magic i'm going to return from our units list where the unit faction equals the faction that we're sending in then i'm going to order the list by random so i'm going to use unity's random class again and then i'm just going to return the first one and we don't actually want to return the whole scriptable unit we just want to return the prefab okay and because this is expecting a return type of t we can cast it to t so this may look a little bit daunting but i'll show you how it is called but first i'll just go over it one time we're going through our list we're saying we want all of them all of the units according to the faction that we're telling it uh that we want and then we're randomly shuffling them around and then we're selecting the first one from this rough ruffled stack and just returning the actual prefab okay so here we can say var random prefab equals get random unit and it's going to be of type hero base man base hero and the faction is going to be hero okay so now that we've got this random one we've only got a snorlax in there so it's going to be snorlax uh we can spawn this now say spawned hero etools instantiate random prefab we don't really care where it's going right now now we need a tile to spawn it on don't we so i'm also going to do this really rudimentary but basically i'm going to split the tiles in half heroes are going to spawn the left enemies are going to spawn on the right so all i need to do is get a tile that uh x that the tile x if we go to our grid manager our tile x is less than width divided by two okay so let's do this let's go public tile get hero spawn tile or something and in here we're going to return now if you remember our tiles is a dictionary and the key is a vector 2 the position and the value itself is the tile so we're going to return tiles where the t dot key dot x is less than our map width divided by two and then let's also order these so so that we always get a random one whoops and then return the first and we actually only want the value okay so get hero spawn and we're going to grab something on the left but we don't want to spawn on a mountain do we and we don't want to spawn on another tile that has an occupied unit so let's create some let's add a little bit of logic here so we're going to need two variables first one is the boolean which will be is walkable now we're going to set this in the inspector so for example the grass is always going to be walkable and the mountain is always not going to be walkable and then we're going to create two publics here so first one is going to be of type unit base oh my golly gosh base unit and this is going to be called occupied unit and then we'll have another boolean here which is like the final walkable check all right so this is going to be called walkable and i'm going to use an arrow function here and this is just going to return it's going to say that it is in fact walkable if is walkable is true okay and occupied unit equals null okay because we don't want to go on another tile that's already occupied do we so now we can just call walkable and it will check all of our things and say yes this tile is walkable at this moment so back on our grid manager so here not only do we need to check that this is on the left side of the map we also need to check that the t that the actual tile is walkable perfect so while we're here let's also just do the get enemy spawn and this one will be more than x and walkable okay so basically the same thing now on our unit manager we can so now we've grabbed our random unit we've spawned it now we need to grab a random spawn tile so then we go grid whoops grid manager instance get hero spawn tile once we've got that tile we need to actually set our unit to it so we could do this we could say spawned hero dot transform.position equals random spawntile dot transform.position that will place him there and then on our uh random spawn tile we could set the occupied unit is indeed the spawned hero and then also it would be good to actually have on the hero what tile they're on so on our base hero on our base unit sorry public uh tile occupied tile let's remove that as we don't need that so then in uh unit manager we can say our spawned hero dot occupied tile equals the random random spawn pile cool but this actually sucks like um we're gonna have to do this for the spawn hero do it for the uh spawn enemies every time one of the moves we have to do this too so it would be nice if we could just like put this logic in one place and just make a function call and we can let's do that on tile so on tile public void set unit and this will take a unit base oh my god base unit [Music] and we can grab this logic here and plonk this right in here so the unit is now going to be this and this is the actual tile so we don't need to even reference the random spawn tile we can just do that and that will be set to this as we're talking about this tile and also when the unit moves we also want to de-allocate his current tile or else all the tiles are going to start getting occupied units even if they're not there so what we'll say is if units oh if unit dot occupied tile does not equal null we'll say the unit occupied tile dot occupied unit equals null okay so we're actually going to this unit's occupied tile and then setting its occupied unit to null so we have to do this if check because the very first time we spawn the unit this will in fact be null okay and we don't want to get a null reference exception so now that we've got this we can just say instead of having all that logic we can say unit manager ins no we do it we can say random spawn tile set unit and then we can send in the spawn hero cool bananas so let's call this from our game manager so unit manager instance spawn heroes and am i actually continuing this flow in grid manager yes i am cool so let's actually try that let's go back into the game group manager and i need to create a game manager here i've only made the script haven't met haven't actually assigned it game manager i also need to make a unit manager and assign the unit manager scripts okay now let's try that uh secrets contains no elements what are you talking about sir so i can't find a tile which satisfies that ah because we have not set our grass tiles to be walkable yet so all of our tiles grass and mountains are not walkable so grass tiles is walkable true and then our mountain child will obviously be false let's try that there we go so it's spawning our stuff it's not spawning our unit or is it it is where is it okay so uh our tiles are on uh sorting so all order layer zero but our units are also on zero so it was being covered so put that on one and there we go let's quickly do that for the enemies as well so in our unit manager let's just copy that and do spawn enemies and then this can be just enemy count and yet again uh this will obviously depending on the level that you're going to you'll go to your level details and you'll see what units need to be spawned and then you'll spawn them and it probably won't be random either you'll probably be like putting them in specific places or not but so this will be the random unit that we're getting is not base hero it is base enemy base enemy and the faction will be enemy and that will be spawned enemy get hero spawn tile we'd want to get enemy spawn tile so we've got a little bit of repeat code here so you could probably uh make it a little bit more efficient like we've got this nice generic function here that we're using in both times uh you could probably make this a generic function as well but anyway so now that we're spawning heroes we need to send it to the next game manager stage change state and that will be now spawn enemies so in spawn and while we're here let's just uh we know that after we spawn the enemies we'll be wanting to go to our hero turn so in game manager uh we the next state will be spawn enemies cool so let's try that that should work straight out of the gate go and there we go spawning at random excellent so now we're heading to the player's turn anyway so it's coming down here and we're setting this game state to hero's turn so now let's add some click logic some selection logic i'm just going to close out all those because it's getting very busy so let's do our click logic in our tile that makes sense to me let's make a void on mouse down on our tile and i'm sorry if you can hear some uh bassy music in the background i have got some seriously uh crappy neighbours had to choose my words wisely though this is a family friendly channel so now on mouse down we only care about on mouse down when the game state is the player's turn the heroes turn so let's just check um if game manager instance uh game state does not equal the player's turn uh hero heroes down let's just return we don't want to do it so there are actually a few branches that we need to check here let's do if occupied unit is not null so if there is a unit on this tile let's do some stuff so the first what we should do is actually check if it is a hero that we're clicking on or an enemy and it would actually be nice if in our actual hero class we have access to the faction so let's just add faction and before i forget i'm just gonna make sure that uh i'm setting them to the right faction so snorlax will be a hero quinn will be an enemy like that okay so if occupied unit faction equals hero what do we want to do well we want to select the hero don't we we want to select it so that we can make a move with it um so in our unit manager let's do this let's make a public hero base as we every time hero base hero uh selected hero so i'm making it a hero as we don't ever want to select an enemy uh we only ever want to make actionable on a on a hero and then we'll create a function down here called public void set selected hero and that will take in a hero and then selected hero equals hero and the reason i'm not directly manipulating that i'm actually sending it in here is because it would be nice if on our canvas we can display like what heroes selected and maybe the moves that they have available okay so let's call that now in tile say grid no unit manager instance uh set selected hero and then send in the occupied unit and we know that it will be our type hero so let's just cast it base hero um yeah usually i put bass on the end of words not at the start so this is it's like throwing me off every single time anyway so uh if they're a hero and we know that any logic in here means that there is in fact an occupied unit so it's either going to be a hero or it's going to be an enemy so if it's a hero let's select it else we need to check if we already have a selected unit so if unit manager instance selected hero does not equal null okay so we've actually got a hero selected here and if there is a selected hero uh we know that the new unit that's being clicked is not a hero so then the only scenario here is that we are clicking on an enemy all right so in your game obviously you're going to show uh when they when they select the hero it's going to show like a uh their walk distance and also their attack their attacked uh squares that they can attack on and you'll have all this logic there but for me i'm just gonna say if you've if you've selected a hero and then you select and then you click on an enemy you're just gonna kill it regardless of where it is okay and obviously on your units you would also have a health system and you'd have a function that takes damage and does all the animations and stuff but for me i'm just gonna delete the enemy as soon as i click it okay it'll still give you the idea of of what to do so i'm going to grab the enemy like this and i know this is an enemy so i can cast it to the base enemy and this will be occupied unit so so now in your game you should be doing something like enemy dot take damage or whatever or uh or maybe you should be doing something like uh this selected hero dot attack and then sending in the unit the enemy unit to go on attack or whatever okay but for now i'm just going to say destroy enemy game object and i also want to deselect our selected units so unit manager instance uh set selected selected hero and i'm just going to send in null now so i believe that's all we need in the if if we click on a tile and the unit and the tile is occupied so now we'll do an if an else statement here we can say if unit manager instance selected hero does not equal null all right so we have got a select we've we've already got a selected unit and we have now clicked a tile that doesn't have a unit on it okay so what we want to do here is move our hero to this tile so we can use uh we're already on our tile so we can just use our set unit can't we we can say set unit and we can send in our selected hero like that so we're going to move our hero to this new tile and then we want to deselect our selected hero after that and i think that is it for that now to actually show that we're selecting a hero let's create a canvas your canvas and i'm going to set my canvas scala to scale with screen size so it's all nice and uniform regardless of the size of the screen and i'm going to create a image and this will be called uh selected let's put this on the bottom left and i'll make it a little bit transparent and something like this like that maybe and i'll add some text under it and this will be i'll just put this as a placeholder selected hero like that and then uh now we need a menu manager so let's create a new object menu manager and create you know managers here menu manager [Music] and yet again i have found myself needing to create a new singleton so public static menu manager instance [Music] perfect so now let's have a function called public void show selected hero and oh my god and this will take in a hero bass whoops a bass hero [Music] all right so we need an object here so let's make a serialized field private game objects selected hero object now i would probably create its own special class for this but for now i'm just going to do this i'm going to say selected object hero get component in children and this will be a text component under the unity engine ui library and i will say text is equal to now it would be nice if we actually had a name for this hero so let's go in here uh no let's go into the actual unit base and create a new variable here called public string units name so then in menu manager we can say all right what the hero is and the unit name and then we can toggle it on to set active equals true and we can also use this very same function to toggle it off so we can say if hero equals null we can take this line and instead disable it and then just return another function as we don't want to run that so show selected hero cool so now in our unit manager so that's handy that we made this function instead of just directly setting it now we can say menu manager instance show selected hero and just sense send in the hero like that cool bananas and you know what while we're doing this menu manager let's actually kind of do the exact same thing but hover effects so let's let's uh duplicate this and this will now be a tile info and let's send it over this side instead and i'm just going to kind of like roughly put it there and the base information will be tile info and then also if the tile has a unit on it let's also make another one and scale this one a bit kind of like sub information this one here will be uh tile units and this one will be uh units okay so then in here let's create a tile info object or let's just call it tile object and then tile unit object and then another function public void show tile info and this will send in a tile and we can say uh let's just copy this so it's going to be kind of the same tile object and this will be the tile and it would be nice to have a tile name too wouldn't it say let's create on the base tile public string tile name and go here tile name and then we're also going to say if this tile has an occupied unit let's copy this and this will be the tile unit this time it will be tile occupied unit dot unit name and and then we're going to do the exact same thing as up here so we can also toggle it by saying if tile equals null then we'll take the tile object and null it and also take the tile unit object and null that too now in our tile see how we're hovering over it here so on mouse enter we can say menu manager dot instance uh show tile info and just send in this and then we can do the same when we exit but we'll toggle it off by saying null okay and then in the same way in our unit manager this can also be null so this this will be toggling it off as well holy moly my throat needs some water so anyway back in unity in our menu manager let's add the menu manager and let's assign these things so selected hero tile info and then tile unit info and just for a little bit of extra organization i'm just going to do this managers uh resurrect that take all of our managers so menu manager unit game grid and then put that at the top perfect so let's press play yep so we don't have names for our grass tiles so i'm going to call this grasslands and mountain and then this will be quinn and this will be snorlax cool bananas let's go so there we go so we've got uh and also let's just to make it a little bit neater disable those to start so grasslands mountain and then it shows snorlax on top and quinn and because we're in player turn we can click this and our snorlax will be selected and then we should be able to click and because we're not actually uh after our unit moves we're not actually like uh moving the turn on we're still in player tone i can i should just be able to click and then destroy quinn so let's just try that again click move click move click move destroy quinn perfect so now you would uh actually progress it onto the enemy's turn and then you would perform some you know logic for the enemies to try and kill the player or what have you alright so i think that's about all i'm going to show you in this video i'm sure this video is going to be super long as it is if you learned something or you enjoyed it make sure you subscribe and uh like let me know in the comments if there's anything else you would like to learn and i will most probably make a video out of it if it's a good idea also i'm going to be releasing a series of these type of things like how to make the base of different specific games so if there's another game type that you would like to know how to make let me know and i'll make a video on it but until then uh happy dev journeys and i will see you in the next video ciao