Make a basic finite state machine (Unity/C# tutorial)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
good morning good afternoon everyone and welcome to this unity tutorial on how to make basic state machine for 2d player movement in less than 30 minutes in this video we're gonna see how to implement the state pattern in unity to go from idle to moving state on a simple square player are you ready then let's dive in today we're going to focus on finance state machines or fsms state machines are a particular kind of automata that are used to represent and study simple machines the idea with fsms is that you have a finite number of states for your target entity to be in only one of these states is active at the same time it's called the current state then there are some external or internal inputs that can trigger a transition from one state to another and so a state transition outputs a new active state it makes the state machine switch from its current state to a new one suppose you have a little avatar for your hero in your game at first it would be in some sort of idle state then if you press an arrow key this is your input it will transition to the move state then if you release the key you will transition back to the idle state similarly you could have a jump state or crouched state and so on updating the current state value may be shown in multiple ways the animation can change perhaps the object starts to move or you could even have a small sound in this tutorial we'll talk about deterministic state machines which means that when you're in specific state and you receive a specific input you'll know exactly what transition will occur and where you end up for your new active state but there are also stochastic stick machines where the transition can have some randomness to it so that you can get different results even if you repeat the exact same chain of events finally you'll see that each state in our fsm will have three important entry points the enter method for any init process that needs to be done when you transition to this state the update method for things that should happen continuously while the entity is in this state and the exit methods for the cleanups to do when you transition from this state each state represents a specific behavior for the entity and in general the state pattern is a nice way of decoupling those behaviors as long as the overall system isn't too complex with this theory out of the way let's get to today's tutorial example and see how to use state machines to have a basic player movement in 2d physics okay the first thing you want to be careful about is when you create a unity project actually choose the 2d preset because for this tutorial we're going to work with 2d physics and an orthographic view so make sure you choose 2d and then create your project alright now before we actually go into the state machine scripting let's first prepare a little scene so that we have something to look at i'm just going to use a basic sprite renderer so 2d sport renderer for the player it's just going to be a square and i'm going to have a little ground block at the bottom so the player has something to interact with so for now if i just look at the game it's just the basic blue screen unity shows us so you can go to the camera and just change the solid color of the background something like white for example and then i'm just gonna add my new game object to the object sprite square so this is gonna be my player so i'm just gonna rename it and i'm gonna give it a black color so i can actually see it on the background and then i'm going to add two components on it the first one is going to be a box collider 2d so this is to actually get the collisions and be able to interact with the other objects and the second component is a rigidbody 2d also remember to always take the 2d option of the two you have a box collider and a rigid body that are for 3d physics here we want 2d physics so the rigid body is what's going to be able to compute gravity and overall apply velocity on your object if you just have a collider you'll be able to get if something hits you but if you have a rigid body you'll be able to have forces and impulses move your object around now the second object we want to create is the ground the floor this is also going to be a sprite runner so i'm just going to go again to to the object sprite and square and i'm going to give it same black color but i'm going to stretch it so that it goes all across the screen so i'm going to increase the x scale something really big and a bit of the y scale and i'm just going to move it down so this one i have my player and my ground beneath it same thing for the ground we want it to have collisions so we give it a box collider 2d but this one we don't want it to have actually any dynamic so we don't want it to have a rigid body it's just going to be a collider and now i'm just going to rename the subject to ground all right so we're now done setting up the scene we're going gonna start and work on the state machine and we're gonna first get it to have the basic idle state and to show us this state so we're gonna have two things we're gonna have the logic and we're gonna have a basic qi display a little label in the top left that tells us what current state our state machine is in so first thing first go to your assets and create a new folder called scripts and in this folder just create a new c-sharp script called state machine and in truth we're just going to go right away and create our second script another c sharp script called base date and this one is for all the states that are going to go into our state machine the base state in itself won't be used it would just be like a blueprint for all our actual states that are going to inherit from this class but this class is not going to be a mono behavior so it won't have all the usual unity built-ins like start date or destroy for example we're just going to have a basic c class and we're going to get rid of all that in this class we wanted to have the three important parts that we discussed before the enter the update and the exit points but for the update we want to mimic um what unity has if you're a little bit burst into unity and its competent life cycle you might be aware that there are actually several updates the void update that we're really used to is the one that's called at the beginning but you then have two other updates the fixed update and the late update and those are called later on in a frame their way of separating logic so that everything runs smoothly and in the proper order because for example physics are computed after some things so if you put your physics computation in the update function it might not react the same as if you put it in the fixed update to mimic this we're going to have two updates we're going to have a logic update and we're going to have a physics update because we know that we want to have some physics in our game let's implement all this and the first important thing is that those functions they're going to be public but they're also going to be virtual and the reason they're going to be virtual is because we don't want the base state to do all of the behavior definition we wanted to just declare the fact that those methods exist but know that the actual implementation is going to be in child classes so it's going to be for example public virtual void enter this is the first method that we talked about then we're going to have another for the exit and in the middle we're going to have two updates that we discussed so we're going to have an update logic and update physics and so as you can see those four functions don't actually have any body we don't have any implementation in those but we know that we can have additional implementation from the children classes because of the virtual keyword here the last thing you want to add is a little constructors that you can actually create a new state and this constructor is going to take in two parameters first a name for a state and then a stat machine so this is the state machine that your state is a part of and so those are going to be used in our fields public string name and a protected state machine this is because um the name we want it to be accessible from anywhere so we can actually print the state name in our ui whereas the state machine we just need in this class and its children classes so in order to have a good data encapsulation it's better to use the protected keyword and so let's just assign those two here so this name equals name and this state machine equals state machine so once again this base state won't be used as is it cannot be used as is because it has those virtual keywords and it doesn't have any logic in it but we'll see in just a minute how to create derived classes that are actual states now let's go back to the state machine and now that we have a base state class we can actually use it to create our current state variable that we talked about all right so we now have the current state and what we want to do is for example in the update um if we currently have a current state so if current state isn't null we want to call its update logic method remember the update is for logic and then we're gonna have another update we're gonna have the late update so it happens later on in the frame this is going to call the update physics method now there are two other things that we want to do first we want to create our transition function so this is going to be called change state and it's going to take in the new state that you want to transition to and this works in two phases first you have to make sure that you exit from your current state so you finish all the things you want to finish then you assign the new state and you enter it this is going to be our state transition and the second thing we want to do is take care of this init phase the thing is that we would like to have current state equals some initial state but we don't have it and the reason we don't have it is that this state machine is still quite abstract it doesn't really care whether you're talking about player movement or ai patrolling or whatnot it's just about cutting logic calling physics and then transitioning so we are going to do our own state machine that's going to be derived from this class and that's going to be specifically for the 2d player movement it's going to be called movement sm for movement state machine and it's gonna get a lot of things from this but it's gonna have its own states so we won't be listing the actual states like idle moving or jumping in this class in this state machine parent class because this class shouldn't be aware of the actual state that you have it should just be aware of the fact that you have states that you can transition from one another and that you can call updates on those states but it doesn't care which states they are exactly and in particular it doesn't know which state should be the initial state so rather than saying something here we're gonna use another protected virtual method that we're going to call get initial state and this function is actually going to be different from one state machine implementation to another so if i just put it here for this parent class it shouldn't be able to know which state to return so it's just gonna return no for now but later on when we actually create our own state machine we'll be able to say okay for this state machine the initial state is this or that all right and just while we're here we're going to do a last thing we're going to use the unity built-in on ui this is going to be able to put things on the screen and what we want to do is just to show a little label so we're going to have some content that is either if we actually have a current state it's going to be the current state name or else we're going to show something like no current state this shouldn't happen but it's just in case we have something we want to have no error another reference now we can have a little gy label that says something like with the content in the middle and we wanted to have a black color and a size wants to be quite big so size of 40. here i'm using unity's rich text feature that allows you to add in some little tags to format how it should be displayed and so if we do that currently we won't get anything because we don't yet have a current state because we couldn't initialize it but no matter what if we do get a state we want to do something we want to enter this state so if we do get a state we want to enter it this class is actually ready now we won't be touching it anymore in this tutorial but we're gonna move now to the actual implementations of all those abstract and virtual and parent classes to get real usable objects so let's go back to our unity editor and create those new scripts the first one i'm going to create is the state machine so it's going to be the movement sm movement state machine i'm going to open this and the first thing you want to ensure is that it inherits from our state machine script so right now it's a child class and it can do all the things that the state machine can do but for now we can actually do anything because we're gonna have states so let's create some go back into unity and create a new subfolder in your scripts folder called states and in this folder let's create a new c-sharp script called idle we're going to start with the basic outer state this is the thing that you should have at the beginning so it's going to be our initial state and this state we wanted to inherit from our base state class let's get rid of all this and this script is going to work as follows we're going to store the horizontal input so this is basically if you just press or release the left and right arrow keys on your keyboard you're going to have some axis some unity input axis that is predefined and that is called horizontal and that's able to know if you pressed the right to the left or if you released it and it's gonna have a float that goes from something negative to something positive if you press left or right or zero if you don't press anything first let's get a little viable to hold this it's going to be a float uh i'll just prefix it with a little underscore because it's a private variable and i know i want to override my enter function so i'm going to have a public override void enter and you see that it's automatically completed with base enter this means call the enter method of my base class so in this state the base state class and after it's done that i want to just say that horizontal input is zero for now because at the very beginning you don't press any key now similarly we can also override the update logic method and this is where we're going to get our horizontal input value for this frame so we call the base update logic and just after that we say that horizontal input is going to be input dot get axis horizontal and once again this is something that is already defined by unity and that we can access as is and it's going to be a float with something negative if you if you're pressing the left key and something positive if you're pressing the right key and 0 if you're not pressing anything so very soon we see that we want to actually transition to the moving state if this input is different from 0 for now we can't do that because we don't have the moving state but this is what we're going to be doing very soon and last thing we want to take care of is our constructor because for now we can't create an idle state because we can construct it from a constructor so here let's just define an idle constructor and remember that we want to pass it a state machine but we know that this idle state is completely linked to our custom movement state machine script so instead of passing it a state machine we're gonna pass it a movement sm state machine and we're going to use the base idle state machine constructor this is just calling the constructor of your base class so once again the base state class and passing it the parameters that you expect so here we pass in our name for the state and the state machine variable since it's a class that's derived from state machine you can pass it in to a state machine variable now let's create the moving state it's going to be really similar for now we're going to add the differences later on we can just actually copy this script and pass it in the folder so let me rename arlo copy so i'm just going to rename this to moving and here you have to make sure that you rename the class and the constructor and change the name so now we have our second state we want to be able to transition from and to this state depending on our horizontal input so remember that in the idle we want to transition to the moving state if our input isn't zero and so in our moving script we want to transition to idle state if the input is somewhat zero we're going to see very soon how to get this approximate value because floats are never actually completely accurate in computer science but you can get something that's really close to zero and so you're gonna say okay it's basically zero i'm just gonna transition back to idle state okay now that we have the states we can use those in our movement sm so in our movement state machine so we're going to have a public idle idle state and a public moving moving state we don't want those variables to show in the inspector even though they're public so we'll just make sure they don't show with the height inspector attribute and now we can implement our get initial state because what we want to do is a protected override base state get initial state and it's just going to return instead of this base get initial state is going to return either state now let's make sure that we do initialize those states with a private vote awake that's going to say that idol state is a new idol using this state machine as reference and moving state is a new moving also using this movement sm state machine as reference and at that point the really cool thing is that if we go to idle we can now do this transition so the first thing we want to do is check that the input is not equal to zero taking into account this little approximation i talked about to do that we'll just use the math apps so that it's it doesn't care about the actual sign of your number my abs horizontal input is more than math epsilon this is a little small value that unc defines for us and that is a way of saying not zero it's going to be more than a little value so it's going to be different from zero and if we have this we want to do state machine so this is a reference to our state machine the state machine that this state is used in and we're going to do state machine dot change state to the state machine dot moving state except that if you do that you're going to have some kind of okay i don't have a definition from moving state and this is normal because state machine so far is still a state machine class instance it's not a movement sm class it could be casted to this and we're gonna do it but for now it can do it on its own so you have to force it by saying movement sm at the beginning to cast it and remember to add the links on brackets here and now it's going to say okay i'm a state machine but i'm a state machine of movement sm type and so i do have a field called moving state similarly you can just copy those lines and pass them in the moving state here and now it's if we're less than math epsilon we want to go back to our idle state just to make sure that we do have some state transition we're gonna add this to our player so let's go to our player and add a new component that is called movement sm now if you run this you're gonna see that at the very beginning you do start in the idle state you have this label here that says you're in the idle state and if you press the keys you're going to have moving we're not actually moving but we are transitioning from moving in either state when we press and release the keys on the keyboard so this is working this is really cool but we want to actually move and so to do that we're going to use our rigid body let's go back to our visual studio and in the moving state we want to be able to get the rigid body so we need to get reference to this in the movement sm script we're gonna have a public rigidbody 2d rigidbody and in unity we can now drag this rigid body onto the movement sm script and then if we go back to our script in the moving we now want to also override the update physics so let's just copy this and use update physics here and the base update physics as well and now what i want to do is to be able to change the velocity of our rigid body we're going to start from our current velocity so we're just going to say that it's our state machine but once again we have to cast it to more than state machine so it's going to be movement sm state machine dot rigidbody dot velocity and then we're going to say that this velocity dot x is something like horizontal input times our speed and this speed we're also going to define it in the movement sm so we could do something like this and then declare a function but you're starting to see that the movement sm casting is a bit tedious so we're just going to put this in a variable in the private variable here i'm going to say that we have private movement assembly it's just sm and here i'm going to say that when we first build our object sm is movement sm of state machine and so now here we can just say that it's sm dot id state and sm dot rigid body and sm dot spin so we're gonna now define the speed variable let's go back to our movement sm and say that public float speed is something like 4f you should just try and see what value fits your your needs i've just found while testing that this one is quite good now here what we need to do at the end is reassign this velocity to our rigid body so it really takes into account our computation and so we're going to say that the rigid body velocity is our vel vector 2. now if we go back to our game and we start it you see that when you press the arrow keys the player is actually moving so this is really cool and the last thing you want to do in this tutorial to see how states can impact various systems we want to change the color of the square depending on the state that we're in we've seen that the state machine is aware of which states we're in because we are little label updates but we want to make sure that other systems on the player could be aware of that let's go back to our movement sm and add a new reference this time to a public sprite renderer and so this is going to be our sprites renderer and now that we have that we can use the enter entry points of both our states to change the sprites render color depending on which states we're entering so let's take this reference in the moving stage we can do it quite easily by doing sm that's why it's rendered that color equals something like colored thread for example in the idle states we have to do the same thing but we haven't created our little sm shorthand so we still have to do our type casting so we're going to have movement sm state machine dot sprite render dot color equals color black and before we run the game we want to make sure that we assign this part renderer so in your movement sm you should just go ahead and drag the sprites render here now if you start the game you see that by default you get this black color as expected and then if you start moving it gets red and it goes back to black as soon as you've stopped moving because it's back to its idle state that's it for today folks we now have a basic two-state machine system for basic physics-based 2d player movement we've seen how to use virtual methods and overrides to define a simple inheritance hierarchy and we've used rigid bodies to add motion to the player if you want to take this further make sure to check out part 2 of this tutorial on adding a jump state via hierarchical fsms don't hesitate to post comments with your ideas on how to improve and experiment with this feature and in the meantime feel free to like and share this video or to subscribe to my channel so you know as soon as new videos come out thanks for watching and take care
Info
Channel: Mina PĂȘcheux
Views: 2,647
Rating: undefined out of 5
Keywords: unity, tutorial, game, fsm, finite, state, machine, physics, rigidbody, 2D, gamedev, programming, state machine, ai, pattern, csharp, c#
Id: -VkezxxjsSE
Channel Id: undefined
Length: 28min 48sec (1728 seconds)
Published: Sun Jul 25 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.