Depth System Tutorial: GMS2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Applause] [Music] [Applause] [Music] hey guys so today we're going to be making a depth system and I have made videos on depth systems before it was actually one of the first couple videos I ever made and I have it in my farming series but it's been a couple years and I kind of wanted to update the system and condense it into one video as well as incorporate some of the frequently asked questions that I get about it which will kind of add a bit of functionality to the existing system so just to begin with I want to explain what a depth system even is what I actually mean by that so a depth system is going to be something that is controlling the depth at which our instances are drawing at so in your projects if you don't have a depth system set up you're going to be getting this kind of behavior so even though we might expect an object higher than another one to go behind it this isn't actually a built-in functionality in GameMaker we have to set this up ourselves and there's a lot of different ways that you can do this my way is going to be specific it's not necessarily the best way or the most performant way it's just a way that I find relatively easy to set up and explain and it allows you to kind of keep the layers which have their own depths you might have realized so for example just in my room right here you can see I have ground which is in these tiles right that's the ground which is below all of the instances and if you look right here that's because the ground is at a higher depth than the instances layer right so anything with a higher depth just think if it's higher than its kind of deeper it's further away so things with a higher depth or further away and instances themselves can have a depth so I could set the player for example if the player had a depth that is lower than the tiles and it's going to appear in front of it which is kind of what happens when you place it into the instances layer for example so every instance in the instances layout gets set to a depth of zero and from then on what determines the order in which they or so if a bunch of things have the same depth then what determines what gets drawn on top the other thing is actually just who runs their drawer event first and you can kind of picture that as if we have a blank canvas and we go ahead and stick a bunch of stickers onto the canvas the sticker that you put on first is going to end up behind everything else and we're going to take advantage of the draw order in this system that I'm going to be talking about today so we're going to have all the instances up the top drawing first and then as we go kind of down the room the instances will be drawing one after the other and the variable that we are going to be looking at to control this is of course the Y variable and as you might know the y-axis starts at the top of the room and as a kind of quirk to game maker but under the hood it does make a little bit of sense the y-axis starts at zero right so zero is at the top and as we go down Y increases so anything that has a low Y should be drawing before things with a higher Y so what we basically have to do is stop the objects from drawing themselves in the wrong order so basically disabled their default draw events and then we are going to draw them manually in the order that we want and the way that we do that is just by making a list of all the instances that are going to be involved in this collecting their Y position and then sorting them by their Y position and drawing them in that order in the correct order and that's all there is to it conceptually okay so to begin with I'm not going to explain the very basics of setting up a project I already have a player character that I can move around and just a few test items that's all I really have in my project that's all you need and the first thing I kind of just want to explain is that remember how I said it's the draw order that is kind of determining what instances draw first by default what controls that draw is actually the instance creation order so right here in the room you can actually bring it up and that can depend on where you place the instances in your layers at different depths so you can see for example that my player is being created after the night and scout so it's actually going to be always drawing on top of them if I run my game at the moment but that's not what we want so what I'm actually going to do is I want a way to draw all of the instances in the order that I want and have them not draw themselves so I'm actually going to have to kind of disable there or in drawer events eventually and we're going to basically do this instead of going through every single instance and having them perform certain card I'm going to make apparent objects for anything that I want to be part of my depth system and I'll just call that P AR underscore depth object right and I'm going to add all the children that I want to be part of the system so my player the scout the night heads and trees as well right so they're all my children and what I want all of my children to do right now is say visible equals false so this is going to mean that they're drawings aren't going to run they have now taken themselves out of the draw pipeline in game maker so they're not going to be drawing themselves and just to show you if I run the game of course nothing at all is drawing themselves so they're still performing their step events right so the player as you can see is I'm still moving around but they're not drawing themselves okay good so that's fine now I'm going to have another kind of controller object and I'm going to call this well you can call it depths order or you can call it draw or master draw or whatever I'm just going to call it depth sorter and the way that I want to manage this is I kind of have to make a list of all of my depth objects keep track of their Y positions in the room and then kind of order that list relative to their Y positions right so and we're actually going to use a data structure for this because this is what data structures are grateful if you haven't used the data structure that's fine I'm going to be explaining everything you need to know as we go but basically so we're going to be using this one a TS grid and it literally will look like a grid so it's kind of like a as it says a 2d array it's got two axes as you know as it that's what a grid is you can set how many columns and rows there are going to be it calls it the X and Y axis and like I said our grid is going to have two columns and it's going to hold the instance IDs right so they're on their unique IDs every instance has its own unique ID like its name in one column and then it's going to store its Y position in the next one and like I said then we're going to sort the grid by the wire position so that we get the Y's that are the smallest at the top of the grid and then we're basically going to go through that list one by one and draw all the instances so that's kind of going to be sticking it on to our game canvas in the order that we want so we're going to be using this function right here so Diaz grid create so let's type that in and now we have to set a width and a height so that's two and now what is our height going to be well it's going to be however many instances there are in the room since that can change during the game we are actually going to have to update this every step or actually technically not every step but every time there is a change in the number of instances in the room so for now we'll just set it as one and we'll resize it later so this function is now going to create the grid and it's going to return the ID of that grid so we have to save it in a variable so let's save it in I'm just gonna call it D s depth grid so the D s is just kind of reminding me that it's a data structure you can call this whatever you want of course and now that we've created this grid a special thing about data structures is that unlike other variables in the game so just really quickly you might not be aware of this but when you use variables in your game or when you create anything basically there is a little bit of memory in your computer that is kind of taken and dedicated to remembering a certain thing so if I just create a variable called name equals cosmo there is a little bit of memory on my computer that is going to be sequestered when the game is running to remember that every time I put name now I actually mean Kozma and once the game has finished running once it's quit or once the object is destroyed then that gets cleared from your computer's memory right it doesn't just sit there forever so then that memory can be reused for something else but data structures don't do this they're not part of that kind of waste disposal system we have to actually dispose of it manually by ourselves so we have to do that in the cleanup event which is really easy we just can write dears grid destroy and then the ID of the grid which is d s death grid there we go so it's really simple it's really easy to use a data structure like that alright so now that we are done so we've created the data structure now we actually have to put all of the information about the children objects into the grid and then we have to sort the grid and then we have to draw all the instances and we're going to do that all in this drawer event technically you could do it in the step event it does actually matter but to keep things simple we can keep it all in this one event so like I said the first thing that we have to do is make sure that the grid is the correct size so I'm just going to make a comment recites grid and now I'm about to be using the named es depth read a lot during this event so I'm going to just create a little temporary variable to make it easier for myself to type over and over again so just D grid or you can just call it grid or even just like G if you want alright and so now when we D s grid resize so we can change the width and height of a certain grid which is our d grid we know that the width is always going to be 2 so we can put that in but now the height so we need to set that to the instance number right so the number of children depth objects that there are in the room and to do that we can just get the instance number of parent depth object because that is going to include all of the children that we put in that object so there we go now we have the grid that is the correct size now we have to actually add the information to the grid and really quickly actually before I move on if you haven't really used these temporary variables before since we've kind of been discussing data structures and different types of variables in memory these are also kind of different to instance variables which are these blue ones right here because they these are these blue ones they will kind of be remembered for as long as this object exists in the game and it will be the memory for this will be destroyed once the object is destroyed temporary variables are a little bit different they are actually destroyed as soon as this script is done running and they also tend to be a little bit faster to access and as well as that they also have a different scope so I can use this instance variable anywhere in the instance so if I came to the step event and I said D s depth grid right it's going to refer to the depth grid or if I use the x and y variable there are also instance variables that are unique to every instance and usable in any event but if I typed D grid over here it doesn't have access to the local variable that we created in the drawer event because like I said that gets destroyed when the script is done all right anyway let's keep going so it's add the instance information to the grid and to do this we are going to take advantage of a with this statement and a with statement is basically a way of getting access to a different instance so something other than what is currently running this script which is the depths order so we can instead of using the dot operator for example if we wanted to get access to obj balls Y variable which like I said before it's an instance variable so we don't have access to it technically if we just write Y it would not get obj bowls Y variable that we get our own one so by putting the dot operator that kind of accesses the ball and another way to do this is to say with the ball and then run whatever code I tell it to so that is kind of jumping from whatever we are in to a different instance and then it is going to run whatever code we put in here and for example if I put parent depth object that will make all instances of parent depth object run the code which is including its children so I'm going to take advantage of that to add everyone to the grid sir now the way that we access a grid is like this so there is actually a function for adding stuff to the grid so dear squared add and then it's D grid the x position which is the x axis so let's just put 0 so remember the x axis is the one going across if we have a width of 2 then that means the very first column and now for y to get the coordinate of that cell let's just say we put 0 and then whatever value we want to put in so let's say that we wrote that so like I said before I want to fill the first column with the instance IDs and the second column with the Y positions of the instances so this would add the first instance to the correct position and another way of writing that in here would be this just a little bit quicker than writing out that entire function right and this little hash thing is called an accessor it's just a way of getting access to the grid different data structures have different ones like a map has a question mark but the one for grids is that little hash now though I don't want to actually just put a number for the y coordinate because every instance that runs this then will be putting their ID in that very first row which is not what we want so we want actually to control this programmatically with a variable that we can increment every time this runs because this is kind of like a loop they don't all run this simultaneously it's going to loop through every parent object one by one and have them run this code so they can actually all effect a variable so we can start at 0 kind of like we might in a loop and then we can use this while I variable and then tell every single one of them to increase this alright so we can say Y plus plus which is the same as putting YY plus equals 1 right and now every single object is going to be adding to the Y variable so the next one that comes along while I would now equal 1 and it will put itself into the next position the other important thing I want to point out is that this would not work if this was not a local variable so if Y was declared out here within the scope of just the depths order when we go grid an object it's not going to have access to it anymore and in fact if I run the game and you can test this yourself it's going to declare an era and actually let's just take this some opportunities to actually add it to the game so I'm just going to add the depth solder in it doesn't matter where and if we just test that right so we're getting an error because why why has not been said in obj Greentree which is one of the children of my parent depth object and that's because this is an instance variable but if we put this right here this is now a local variable and the scope of that variable is this script anything in this script will have access to this variable it doesn't matter if we go into a with statement anything in this script has access to this variable so it's kind of a sneaky way of passing variables between objects and it also goes for if you declare something inside here once we pop back out we would then be able to access it still all right so anyway we have added the ID of the objects and as that runs through that's actually going to fill in all of the objects IDs and now let's add their Y positions to the second column and I didn't point this out before but just like arrays data structures start there indexing at 0 instead of 1 a lot of things in programming I like that you might have noticed but that is an important point to remember so in the second column let's put the Y the objects okay so that is going to loop through every single object and add them to the grid and in fact at this point because I encourage you to test as much as you possibly can if we hit f6 we're going to run the game in debug mode and nothing is still going to come up but we can actually check if our data structure is getting filled correctly so we just have to find I had quite a few instances so it's a little bit tricky it's right on the bottom the depths order you can see this has appeared now do you can see the index interestingly of data structures is actually just an integer it's not an ID or anything but if we view this as a des grid and enlarge this you can see that's our grid it has a width of 2 and a height of 62 that's the number of instances that I've got in my room and you can look through the individual columns you can see there are all my instance IDs that's what an instance ID looks like and their white positions are here so it's good hopefully you see something similar like that that is a quick way to check the contents of variables in your instances using the debugger I find that especially helpful for testing data structures you might realize at that point that the grid wasn't in any particular order for the wire positions we had just kind of gone through all the objects in the room added all of them to the grid but they weren't in the right order so now we'll sort each row of the grid so that the instance with the lowest Y positions have stuff in column why not column two but with the index of one of course so that they end up at the top and we can do this really easily there is a handy function which is kind of why I chose grids in the first place to use instead of arrays or something is that they have functions for sorting so we can say D grid and now it's asking what column do you want to sort by I want to sort by column one and then it's asking do you want to sort in ascending order yes we do true so again if you run that in debug mode one more time and have a look at your grid it should now have the rows in the order that we want now all that's left to do is to sort through that grid and draw all the instances so now we are going to do a very similar thing to what we did up here but we are going to use an actual repeat loop this time so I'm going to reuse that YY variable that I had before I'm going to set it back to zero I'm gonna use a repeat loop and now the number of times that I want to repeat is going to be just the number of instances that we have right I just want to pull out the instance ID go with that object and tell it to draw itself basically so we just kind of have to pull out the ID get access to that instance just like we did with the with statement and draw it and of course we do have to use a repeat statement we can't just use the with statement again because that's not going to go through the instances in the order that we want we want to kind of determine the order that we're going to be looping through the instances this time so we're still going to repeat by instance number we are still going to be incrementing y/y each time because as we get access to the grid we will be sorting through in an ascending order so the first thing we do is just pull out the ID and we can just save that in a variable so I can go bah rinsed equals and then grab it from our grid so remember the hash in the square brackets so remember the instances were in column one which has an index of zero and the row will be whatever YY is right and technically we don't have to be declaring in Stover Ann Arbor again and every single instance we can actually do this outside right here far in stand we don't even have to set it to a value right here we can actually just use it inside all right so just kind of made it available here so now that we have the instance ID we just have to get the instance to draw itself so we just go with the instance and now previously had told you guys to basically just do draw self and indeed that is kind of the default draw event for instances and if we run the game everything is actually going to work at this point and if you have a little walk around you can see that the depth is working but if you remember at the start I actually had something different in my players draw event so if I come to I'll just show you so basically I had two things that it was drawing it was drawing not only itself which is kind of this is equivalent to just draw a self I don't know I didn't just put that and this also draws a shadow of the instance but that is not currently what it's doing it's not drawing the shadow it's only drawing itself and that's because the draw object isn't telling it to run its draw pen it's just telling it to run this code and we could have said anything we could have said draw a circle but perhaps you actually want it to run its own drawer event so we can go event before evey draw that's the type of drawer event and actually I'll just quickly open this up just to clarify what that function does so we can actually tell it to do any event and just as a note as you can see here this event can't be forced outside of the drawer event so if I was in the step event currently of the depth sorter and I tried to draw something it's not going to work so this has to be called from within the drawer event so now we can access evey draw begin draw ends and everything so if you have stuff in your drawer begin or draw ends of your instances you might want to add that to this system and insert the fact that you want them or want specific objects to draw their events in here but to just access the normal drawer event that's what we would do so now if I run this you can see that my player object is working correctly so it is currently performing its drawer event and I wanted to show you this as you can see everything else has disappeared just because I know a few people have trouble with this one I tell them this fix but if you don't actually have anything in the drawer event of your other objects so like this this one doesn't have a drawer event you're basically telling it to run its drawer event which currently has no code in it so of course nothing happens so what we might want to do is put a drawer event in your parent depth objects and we can just say draw self so now if the object doesn't have an event it will inherit that from its parent so you can see it's coming up now and now everything should be drawing themselves there we go so that is now working so that is everything that is how to set up a little depth system in your game and in the next video just because it has been requested a few times I also want to go through adding a jump to the system and it's quite a quick fix it's basically just adding a what's called a Zed access to the game so if you're interested in that then go ahead and check out the next video in the meantime I hope you guys are all well and I will see you next time [Music]
Info
Channel: FriendlyCosmonaut
Views: 22,347
Rating: undefined out of 5
Keywords: gamemaker studio 2 depth system, gamemaker studio 2 depth tutorial, gamemaker studio 2 depth, depth gamemaker studio 2, gamemaker studio 2 tutorial, gamedev depth, gamemaker depth, gamemaker studio 2, depth friendly cosmonaut
Id: 8QCgN2RDA9I
Channel Id: undefined
Length: 25min 16sec (1516 seconds)
Published: Wed Jul 03 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.