Udemy

The State Pattern in Design

A free video tutorial from Penny de Byl
International Award Winning Professor & Best Selling Author
Rating: 4.6 out of 5Instructor rating
30 courses
142,983 students
The State Pattern

Lecture description

This lecture introduces the state design pattern and starts setting up a project for building a finite state machine for a patrolling and attacking NPC.

Learn more from the full course

Design Patterns for Game Programming

An exploration of robust modularised code building for games with Unity 2019 and C#.

08:59:52 of on-demand video • Updated May 2022

How to create robust, reusable modularised code for use in games programming.
How to implement repeatable solutions to address common game programming problems.
English
If you've worked with character animation or Non Player Character behaviors in a game, chances are you have implemented 'States' at some time or other. States allow game objects to take on different behaviors when required, making them look like they are controlled by a completely different class. Unity makes use of States in the Mecanim System. In the animator, you can create a simple graph of States from individual animations and define how to get from one to the other State. Finite State Machines used in games are the embodiment of this design pattern. A Finite State Machine is a conceptual machine that can be in exactly one of any given number of finite States at any time. A Finite State Machine is used in the field of Artificial Intelligence to define, among other things, the behaviors of computer-controlled characters. It is represented as a graph with nodes that represent the States and transitions that define the path from one node to the next. Once a State has been entered, the N.P.C. stays in that State until a condition on the transition is met. Here we have a Finite State Machine for simple guarding behaviour. If the N.P.C. is in the patrol State, it will continue performing the associated behaviors until a condition is met on any of the transitions exiting the State. In this case, it is waiting until the player is a distance less than 20 away. Once that condition is met, the N.P.C. transitions into the 'chase' State. Any State in a State Machine has 3 processes that it runs at different times. It has an 'Enter', it has an 'Update' and it has an 'Exit'. 'Enter' runs as soon as the State is transitioned to. The State then enters the endless loop of the 'Update'. Update continues to run until the 'transition out of the State' is triggered. At this point before leaving the State, the 'Exit' method is run. For example: an N.P.C. that is transitioning to the 'patrol' State on entering, will need to set the appropriate animation, as well as determine where the waypoints for the patrol are. During its Update, the N.P.C. will simply walk from one waypoint to another, constantly checking when a waypoint has been reached and then looking for the next one. Something in the Update must cause the Exit to fire. This could be something as simple as the N.P.C. deciding to have a sleep. The walk animation is stopped and the N.P.C.s transition into the next State (that takes care of the new behavior of sleep) will begin. A State Machine can be as easy to implement as an if-statement. For example: let's say you have an N.P.C. that you want to be in either of 2 states, Idle or Walking. This would be easy to handle in an Update loop and you've probably done this yourself before. In fact, the code that you see here is way too verbose for what you actually want to do in such a simple task but it is pulled apart to illustrate how States work. So, first of all, you have a State 'flag' that records the State that the N.P.C. is in. Now, in this case, I've used a string but you could easily have an integer or even use an enum, which would make it more meaningful. Now you could use a keypress to indicate whether you want to WALK or whether you want to be IDLE, which would then set that State flag. Later, in the Update loop, we test for the State. So as the State is changing, if it wants your N.P.C. to be walking, then it's going to play a walking animation and actually translate the character in a forward direction, whereas if it's idle (the State), then you want to play the idle animation and you DON'T want to move the character. If however, code outside this class is able to set the State to DEAD, the keypresses inside the Update would overwrite this and you would have to deal with it by a little bit of refactoring, which might look like this. Note how we are testing for the State being 'DEAD' inside the Update before doing anything else. Ok, so this isn't too messy but once you start adding lots of other character States and have them mixed together, writing a State Machine in this format gets way too over-complicated, therefore, the State pattern comes into play where a State is defined as a contained piece of code that deals with all of the behaviors and ONLY the behaviors that belong to that particular State. Instead, we are going to write our own State-based class that all other States will inherit from. A State records the type of State it is, which will entirely depend on the type of State Machine you are writing. In this case, it will be used to build a State Machine for an N.P.C. guard, who will patrol an area and pursue and attack approaching players. Remember the 3 phases of a State? Well, they are Enter, Update and Exit. The phase will dictate which method to run. You can see that there are methods for each of the stages and the base methods update the stages accordingly. Last but not least, there is a 'Process' method. This is called from outside of any State and it determines which method inside the State should run, at any time, based on the set 'stage'. Through the use of this base class as a template for all States, we can be sure to keep the code related to each State compartmentalized from other N.P.C. behaviors. It really is a quite clever design pattern. Ok, enough of the theory, let's start building this thing. Once you get some code together and start creating more States (inheriting from that base class), you'll see just how powerful this design method is and how it will make your life creating A.I. behaviors in your characters so much simpler than having to juggle which State they're in and which animation is playing, which animation is not playing, which key is pressed and all this sort of stuff because it can get really, really messy very, very quickly. Right. So open up a new Unity project. Now I want you to bring in the starter file that is attached to this lecture as a resource. Now it was created with Unity 2019.2 (one of the beta versions, as it was when I created this). I've actually opened it in the current official released version, which is 2019.1.0f2 so I actually went backwards a bit. Now, if you want to do that, it still will work but you'll get a little error that comes up in the console about your NavMesh because there's a NavMesh on here. If you do get that, all you have to do is go into the console and just clear the error message. If it keeps coming back, then just go over to the Inspector. Now I've got the Navigation tab up here as well and if you've opened up my project, I believe you will get that as well. If you don't, just go to 'Window > AI > Navigation' to open up Navigation, go to the Bake tab and then just click on [Bake]. Now that will rebake the surface of the NavMesh showing you the walkable areas in blue and you can see here that the structure is excluded and the area around the trees as well. Now Ugg, who is our guard (let's get in a little closer to him... zoom in here... there he is), ok, so he is guarding our structure. He's going to go on patrol and he has a Nav Mesh Agent component attached to him which will do all the work for us, for him moving around. He has an Audio Source for the gun shooting and his Animator is all set up for you. If we have a quick look at that Animator (so let's just double-click on the Ugg Animator and just open this up, press 'F' to center), you'll see that there's an idle (so there's a Rifle Idle), there's also...up here a Rifle Walk, Rifle Run and a Firing Rifle and there's a few other things down here that we'll get to a little later on. This, in itself, is a State Machine because it has animation States and these are the transitions that trigger between them but we're not writing this, so this is just the animation one but you'll see if you click on any of the transitions, so if we're going from Rifle Idle to Rifle Walk, then you have to trigger the 'isWalking' condition and we will do that all in the code so you don't have to set that up, it's there ready to go for you but there's no code currently on Ugg. The first thing that we're going to do is write a Singleton class for the game environment because (like our previous projects that we've created) there are some checkpoints set out on the surface and you'll see them in the scene. If we go up here and if I just click on all of them, there's 5 of them in total and there they are. So they give Ugg a circuit to go around. Each checkpoint has a tag of 'Checkpoint' (with a capital 'C'). Now what I'd like you to do is write a GameEnvironment Singleton class that picks up all of these and puts them into an array that we can use in our A.I. So we've already done this in previous projects. I want you to do that again. So pause the video now. The code's quite short. When you've done it, come back and I'll quickly put that together and show you how it's done. Alright. So back in our Assets, what we're going to do is create a new script, so 'Create > C# Script' and this script will be called 'GameEnvironment'. Open that up and then inside we're going to create our Singleton. So this won't inherit from MonoBehaviour and it's going to be a 'public sealed' class, like we've made before and then we want to create our lists to hold those checkpoints. So, first of all, let's create a 'private static GameEnvironment instance;' for our Singleton and then a 'private List' of GameObjects: 'checkpoints = new List' of GameObjects and then a public call for that: 'public List' of GameObjects 'Checkpoints {get {return checkpoints; } }'. Ok, we don't need a Start and we don't need an Update, so we'll get rid of those and then all we need in here is our Singleton. So you'll put in a 'public static GameEnvironment Singleton get { if(instance == null)' then we will create it: 'instance = new GameEnvironment();' and then we will populate those checkpoints for the actual instance itself, so 'instance.Checkpoints.AddRange(' and that range will be: 'GameObject.FindObjectsWithTag' (the plural one) and we want "Checkpoint" (the tag that has the capital 'C', just Checkpoint not Checkpoints) and finish that off. Ok and then we will return the instance and then make sure we have enough closing brackets for everything. 3 of them at the bottom there. Right. So, how did you go with that? That's your game Singleton that we can now use to grab those checkpoints and when we start creating the States, as you'll see in a moment and the State classes that are so nice to have a Singleton that you can call upon, you don't have to pass it around between everything. Ok, so I'll leave that video here. When we come back in the next video, we'll start work on our State class.