Object States | Game Maker Studio 2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys today we're talking about object States and how these can be used in our games now conceptually States are quite simple we use them all the time to define our own state of mind such as being awake asleep happy or sad and we know that there are different properties and behaviors associated with these different states you can move around when you're awake but not really when you're asleep so there are things you can and can't do in different states so in our games we can create our own States for objects and have them behave differently depending on what state they're in and this is really useful not just for organizing your project but for creating more complicated and contextual behaviors which you might describe as artificial intelligence now you may have also heard the term a state machine or a finite state machine and that just refers to the whole system that manages the sets so by saying machine we mean the system is relatively complex and self-sufficient so the object is capable of acting different in different contexts and we might even say it's like a little automaton with a primitive intelligence all you have to do is switch it on and the rest will just happen like clockwork because if it were making its own decisions now a really great way to illustrate how states work is with a flowchart so we're going to draw one up here and take some classic examples of enemy states as our point of study so let's make an enemy type and we'll give it four possible states that it can be in so we'll give it an idle state a one two state an alert state and an attack state so in the idle state we'll just say that its behavior is to stand still and do nothing and in the wonder state we'll make it move in a random direction in a lot of games this might be the patrol state as well next in the alert state will say its behavior will be to chase the player and finally in its attack state we'll have it take a swing at the player so so far we've described the enemy States and the behavior we want it to perform in each state but we haven't said how they relate together how it's going to change from one state to the other and we can call these conditions or triggers to change to another state so our states have to not only contain instructions for what the object should do but also have a check for these triggers or conditions to move to another state so for example while we're in the idle state we could set up a detection radius and say if we detect the player within that radius will switch to the alert state and when we're in the alert state we could have another smaller detection radius for attacking the player that way we're able to switch states in this sort of direction now we're getting somewhere but we need to add a way to get back to previous states at the moment if we end up attacking we're just going to stay in that state forever so we have to complete the loops here so we could say once the attack animation is finished we'll return to the alert state just automatically ready for another attack if we detects the player still in our little radius and when we're alert we'll say that we'll return to the idle state if the player goes outside of our larger detection radius and finally we have our wander state where we just move around randomly and there's a lot of ways that you could do this but let's just say that after about three seconds in either the Idol or the wonder state will have a 50% chance of moving to the other state we'll also want to add the same detection radius to our wandering state so that we can transition to the alert state here too all right so this is looking pretty good now we can see how all of the states are related and how we could actually go about coding this so the basic code logic first state is just going to be if we're in this state do that state's behavior and there's a few ways we can accomplish this using GML in game maker so in our projects we can have a variable called state and be checking what it is equal to to determine our behavior so every step we can use if statements to check what the state variable is currently equal to and then wrap all of the state dependent code in those conditions and alternatively you can just use a switch statement here to accomplish the exact same thing those two bits of code are essentially equivalent it just gives you a different way to set this out but back to the if statements and I just want to point something out so before I said we'd be checking what state is equal to but what exactly are we checking for at the moment I'm just using words here but this isn't proper code so we could use strings for the possible states right I could be checking if state is equal to the idle string and that's totally fine that's a viable way of doing it it makes it quite readable but another way you might come across is to just use numbers and the reason you might want to use numbers instead of strings is just that it except a lot less memory but of course this is going to make our code a lot less readable you would have to keep an index somewhere to remind you what number denotes what state but there is another alternative which is my personal favorite for this kind of thing so we can use what's called an enumerator or an enum and think of an enumerator as a way to create our own kind of datatype and this is how it works so if we're going to declare an enumerator we begin like this we say enum and then whatever we're gonna call the enumerator States and basically what we're gonna do is outline all of the possible states we can be in so idle wander alert and attack so now the way enumerators work is we can manually give them a value like on the right or we can just comment them on the left and those two are actually going to be equivalent because if you don't specify what the value is and you just come with them it's gonna automatically assign them numbers in chronological order starting from zero if we want it of course we could make them equal to two seven a thousand a million and it doesn't really matter in this context but we might as well just keep it simple and uses chronological numbers so now to actually refer to one of the states in this enumerator we would write States dot and then whatever state and now remember it so states dot idle that is equal to zero it's as if you had just written zero there and in states that wonder that would equal one it's also worth mentioning that enumerators are global in scope which means they can be accessed from any objects so you can just declare these once say in a controller object and all objects can use the same indexing system here so you can outline all of the possible States it's a great way to keep things simple and centralized and another great thing about using enumerators instead of numbers or strings is that now since we've kind of set up our own data type it's going to come up in the autocomplete so we don't have to remember if we called our state alert or chase or whatever we can just write States dot and an autocomplete list will come up for us so it's sort of a compromise between using the strings and the numbers but it makes it a lot more readable for us and another great thing about enumerators and setting up an indexing system is that you can use it for so many things now so for example we could set up a sprite array that stores the appropriate sprite for each of our states so for example if I have an idol sprite for standing still and then a moving sprite for my wandering and alert and another one for attacking so I could store those in a sprites array but actually instead of just using the numbers here I could use the enumerator because remember all of those are equivalent to those numbers but it makes it really clear what that sprite is for and now in my code when I'm changing the sprite index to be the appropriate sprite depending what state is equal to I can access the appropriate sprite from my sprites array so at any point in time state will be equal to state start idle or states don't wander or alert or attack and you could do exactly the same thing for something like state dependent dialogue so we could use arrays within arrays or a combination of data structures for this but as an example so we could have a dialogue array just like before and then that array itself would be storing another array with the available dialog for that state so for example for the alert we just say hey or stop and then for the idle state we have a couple other dialogue options and when it comes time to actually say something we could for example just choose randomly from the array at that state index in the list but this is getting a little bit more advanced don't worry if you have no idea what any of that array stuff does it's not really the point of this tutorial I just wanted to illustrate some examples of what you could do anyway let's come back to our enemy States example so we're going to jump into game make and now if you haven't already and setup the state system using the enumerators that I described so here is my project and I just have an object's called obj enemy type right here and I'm not gonna go through everything in this project because this video is more about concepts than anything but I'll explain everything as we go so in your create event you should have this set up currently so this is for declaring our enumerator and really this should be in a controller object because presumably you're going to have multiple enemies in your game and you don't want them all declaring this enumerator you only have to do it once but I'm just going to keep it here for simplicity I'm only gonna have one enemy type object running around and of course you also need the state variable and I've just set it to start off in the idle state now coming into the step event so I've set this up exactly how we discuss it previously I have a bunch of if an LC of statements and I'm checking if that state variable is equal to all of our states here and again just a reminder that you could also use a switch statement and in here I have already set up all of my behavior code just using the handy region so if you didn't know about this you can just write hash region here and then an end region somewhere alert and it will allow you to wrap and unwrap that entire region of code so you can hide it so I'm just gonna briefly describe this code for the behavior I outlined before you can skip ahead if you just wanted to watch this for the conceptual stuff I'll leave a timestamp in the comments but this is just if you wanted to know how I've gone about setting up this behavior so for our idle behavior and I've broken this up into three kind of sections so that we have the actual behavior the triggers to move to different transitions and down the bottom I'm just also setting the sprite so for our idle because we're standing still we're not really doing anything all I'm doing is I have a variable that I actually declared in the create event here starting at zero and this is what we're using to keep track of how long we have been in this state so remember how I said every three seconds I'm gonna do a 50% sort of random coin toss to see if I should change to the wandering state the way I've set this up is counter is going to be incrementing every second which means every frame which means it will be incrementing 60 times every second because my room speed is set to 60 the 60 frames in this game so every second it's going to go up by 60 so we're gonna check if our counter is larger than our room speed which is 60 times 3 that is going to be 3 seconds so this variable here is going to be assigned the value of 0 or 1 randomly and we just check here using a switch statement in the case that is equal to 0 I'm going to change the state to the 1/2 state and in the case that it's 1 that means we kind of failed the coin toss and we're gonna stay in the idle State there anything I do here is I reset the counter to 0 so we'll basically go back to here and wait in another 30 seconds and do this all again and the reason I've left off the break here is because I also want to be resetting the counter if I'm changing to the wandering State okay so that would be the transition to the oneness State but we also have another one which is our detection radius so I'm just using the function collision circle here I've set the radius to 64 and of course we're checking for whether the player is within our radius if it does find it there then we change our state to the alert state and finally of course I'm changing the sprite index to the enemy's idle so that's the idle behavior and for the wandering similar to the idle I have the same kind of counter system here setup and the random coin toss flip but because we're wandering we're also going to be changing our x and y position right we're moving around the room and the amount that we're moving is just going to be equal to this variable and what I've done is I've kind of set these up to be a random direction so in here for my movement code these are the relevant variables so I have a speed of just 0.5 that's how fast the enemy is going to move and what I'm having it do is just choosing a random direction to move so this function here is going to get a random integer between 0 and 359 so it can move in any direction but the problem is there isn't really a way to tell game maker to move in a direction it can only really talk in terms of horizontal and vertical movements so I have to translate my kind of vector here to horizontal and vertical movement and we can use these functions here this length dur and length there are Y functions to get the horizontal and the vertical components of that vector all we have to do is give it a length and a direction and of course the length is just going to be how far we can move in each step and that is just going to be our speed variable so in addition to setting it that one time in the create event I'm also kind of re-rolling that directional change if I'm staying in the wondering state I also am going to just randomly change my direction again and in every step it applies that horizontal and vertical movement to the X and Y and just like the idle I also have the same detection radius set up here to change to the alert state and down in the right I'm setting it to the walking sprite which I called SPR enemy and I'm also just setting the image X scale to be relative to our movement so basically if I'm moving to the left if the sign of move X is negative because that's what sine does it makes it either minus 1 or 1 so if my move X is any negative number this is going to be equal to minus 1 and we will be looking to the left because my sprite defaults through looking to the right and if it is a positive number we're moving to the right and I want to be looking right so the image X scale will be 1 okay that's it for our wandering so moving on to the alert state so our behavior here is to chase the player so this is quite similar to the wandering state only instead of just choosing a random direction we're going to choose a very specific direction it's going to be heading towards the player and we can get the direction between two points which is going to be wherever our enemy is and where our player is using the function point direction and again because our movement is confined by our speed we can't move any more than our speed variable in a step we're going to get the vector that has a length of speed in the direction of our player and then we just apply that movement every step so for our transition triggers remember the alert stage had two kind of detection radiuses so not only are we checking to see if the player is still within our alert radius our large alert radius because if it hops back out of our alert radius we're just going to return to idle so if it doesn't detect the player here we return to idle we also have a smaller detection radius for attacking so if our player is very close to us we're going to change our state to the attack and actually pick a swing at the plan so finally in the sprite I'm doing the same thing as with the wandering I'm setting the sprite index to moving and I am setting the direction of s by with the image x scale okay and finally for the attack and now I don't want to go too much into this one because it's gonna be extremely dependent on how your attacks work and also the sprites that you're using for me what I have is kind of an attack objects called obj slash and basically whenever I'm attacking I'm gonna be creating that object and that object has its own code for detecting whether it hits another objects and basically if it sees that that object is not its creator it will do damage to that object and that's why I'm sending things like the Creator over to the slash object so that's just so it can know who its creator is so it won't harm them I'm also sending over our image X scale here so that the slash will be facing the same direction that we are and you'll probably note that this entire thing has been wrapped in a condition if my slash is equal to no one and basically in the create event again I have setup this variable called my slash by default it is equal to no one because basically I don't want to be creating like 600 slash objects while I'm in the attack State because remember this code is running every single step of the game so I don't want to keep creating slash attacking objects I only want to do it once so basically I say if my slash is equal to no one then I go ahead I create slash object I set its ID equal to my slash because this function returns the idea what it's creating I go ahead and do all this initialization I set the image index of ourselves to zero so we're at the start of the attack animation and it will only do that once for this attack base I've also set up the slash object to destroy itself once it's finished its attack and it will reset that variable for us so down in and transition triggers so remember for our attacking what we're doing is just as soon as we hit the end of our attack animation we're gonna just go right back into our alert state which is what this says so if our image index which is the current frame that we're on is larger than our image number and then minus 1 because our image index starts from 0 so if for example we had 7 frames the 6th frame would be the last one so I have to put minus 1 here so if it reaches the end then we go back to the alert state and of course down the bottom I'm just setting the sprite index to P D slash all right so that's it for the behavior so now if I run the game this is how it's gonna look so here is our enemy right there so you can see that it's in its idle right now and it just changed the wandering sprite and chose a random direction and you can see if we get close to it it starts chasing us like that I set up something so we can just visualize its radiuses so there we go that is its alert radius if we go inside it it starts chasing us and then if we hit its attack radius it attacks us all right now lastly I want to show you another way of setting all of this up that can add another layer of organization to the project so instead of putting all of our code in this one code block here and using these if checks or a switch statement to decide which code to run we can actually just put these all in separate scripts so let's just pull all of these out and put them into scripts so let's grab all of the behavior code and cut it out and we're gonna create a new folder in scripts will call the States and I'll add another subfolder for the enemy and finally we'll create a script and this one is going to be called enemy States I'll let me describe that and just paste it in there like that and we'll do the same for all of the other states you all right there we go and we could actually just replace all of our code right now we've just calling the script like that and that would be equivalent to what we had before but that's not really why I wanted to show you this so now that we have all of them in scripts it would be good to just run the one scripts that state is set to so I am going to come into the create event we're going to set up a state's array and each place in this array is going to be indexed using our enumerator system and each place is basically it's going to store at the scripts that we want to run for that state so for the idle State I'm going to be storing that enemy idle script and we can use this same system in all of our objects only we're going to be setting them to different states if we want them to behave differently so let's go ahead and fill all of this in and now remember our state variable is going to be changing its value from states that idle to states that Wanda to alert and attack and remember these are equal to zero one two and three so we can use the state variable to access the appropriate script in our array so if I say state here it's going to be accessing these scripts so now instead of doing any checks all I have to do is say run the scripts and then one of the scripts in our states array and just like that it's going to access the appropriate entry for whatever state that we're in so there we go everything should work exactly the same as before all we've done is sort of change how the project is organized and on that point if you're watching this and wondering well if it all works the same does it really matter how I'm doing it or maybe you got a little lost as we were going through and think it was easier back home we just had it set up with the if statements and strings and if that's what you're thinking that's totally fine use the strings it will work exactly the same a lot of this is just going to come down to preference organization and experience the reason the enumerators are really cool for example and using them as an index in multiple places like with our scripts array or with the dialog as I was discussing before is that it generalizes the system if we change something later you only have to change it in one spot and obviously there are many more ways you can take this further you could add a parent system for multiple enemy types generalize the sprite changes in two variables so that each enemy can have its own sprites have different idle and wandering States for different enemies and also just transfer any repeated code into scripts like a collision check for example so that you're not repeating code you can also use States and state machines outside of AI and your intractable objects for things like game states pausing menus and so on so I hope that gives you an idea of what states can do and why they can be so helpful but that about wraps things up for today's tutorial so today's topic on object states was actually chosen by over on patreon so I'd like to thank all of my patrons who voted and who are supporting me to make these tutorials also a special shout out to 3d monkey biz Daniele Hargrove max Molinar and ethyl Ian for their support I hope you guys are well and I'll see you next time
Info
Channel: FriendlyCosmonaut
Views: 102,102
Rating: undefined out of 5
Keywords: states programming, state machine programming, finite state machine, states gamemaker, states gamemaker studio 2, states tutorial gamemaker, state machine gamemaker, finite state machine gamemaker, state machine tutorial gamemaker, states friendly cosmonaut, state machine friendly cosmonaut, friendly cosmonaut tutorials, gamemaker studio 2, gamemaker studio 2 tutorial, gms2, gamemaker, friendly cosmonaut
Id: DYkJ91eg67Y
Channel Id: undefined
Length: 22min 21sec (1341 seconds)
Published: Tue Oct 03 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.