With our abstract class done, we can now create our Player Movement State Machine. This State Machine will of course take
care of all of the Player Movement States. To create it, in Unity, go back to the "Scripts"
folder and create a new folder named "Characters". Then, inside, create another one named "Player". Inside of that "Player" folder, we'll have
yet another folder named "StateMachines", followed by another folder named "Movement". In there, create a new C# Script to which
I'll name "PlayerMovementStateMachine". Open it up and remove the default methods and swap the "MonoBehaviour" inheritance with "StateMachine" instead. This State Machine can now be used to
change between Player Movement States and run their logic. Of course, we don't really have any State
yet as we still need to create them. Before we do that though, lets
first take a look at the differences between Caching States or Instantiating New States. Whenever we call our "ChangeState"
method, we need to pass in our new State. We can do this by either creating a new instance
of the new State Class every time we Change States or create it once and cache that instance in our State Machine. Creating a new instance every time we change
States ensures that if a state isn't being used, resources won't be wasted unnecessarily, as it will be removed. Caching the instance will always have
resources, like memory, being used, as the instance is still needed
and won't be automatically removed but, you won't need to
instantiate new States everytime. One thing to note here as well is that creating a new instance always creates the variables again so their values
will be reset to their initial default values, while caching the instance will
always have them the way they were. Although, I don't think it
will matter for us too much. Now, which we should use depends in our use case: When a State will be entered quite a lot,
then caching it might be a good idea. Lets take the example of the Player Jumping. It's extremely common for someone to keep pressing
the Jump button while roaming around the map. This means that the Player State will be
changed to the "Jumping State" quite a lot, which makes it a good reason to cache that
State instead of instantiating it every time. Now, lets take an example of
an NPC which has 2 or 3 states that only get changed every 30 minutes. For example, one would be "Idling",
another one would be "Walking" around and another one would be
"Talking" with another NPC. Because they only change every so often,
there isn't much reason to keep them cached and would probably be better for us to
create a new instance at every state change, or, every 30 minutes. This would also make it if there were 100 NPCs in
the map, we wouldn't have 300 States cached in, although we would have 100 States
being instantiated every 30 minutes. Of course, you can mix both usages if you prefer. For our Player however, we'll be caching in all of our States. Now that we know that, lets start by
creating our first Movement States. We'll start with the "Moving States" without the
Group State, but including the "Idling State" and the "Movement State" as well,
as that will be our Base State. So, back in Unity, in the same folder,
create a new folder named "States". Inside, we'll create a new C# Script, to
which I'll name "PlayerMovementState". When that's done, create another folder
named "Grounded", for our "Grounded States". Inside, create a new C# Script
named "PlayerIdlingState". When you're done doing that, create yet another
folder named "Moving", for our "Moving States". Inside, we'll create 3 new C# Scripts:
"PlayerWalkingState", "PlayerRunningState" and "PlayerSprintingState". When you're done creating them, go back 2 folders
and open up the "PlayerMovementState" script. Remove the default methods and swap the "MonoBehaviour" inheritance with an "IState" interface implementation. If you're using an IDE, it should
show an error that says that we should implement the interface methods,
so I'll press "Alt + Enter" and implement them. To make it easier for us to know what
State the Player is currently in, we'll log the current state
class name in the "Enter" method. To do that, type in "Debug.Log();"
and pass in ""State: " + GetType().Name". This will show the name of the correct Class Type. Other options like "typeof()" or "nameof()"
would always show the parent class name. When that's done, we want to make sure we can override these methods with
their own logic in each state, so we'll need to add the "virtual" keyword
to every single one of the methods. Then, we'll open up all of the
other 4 States we've created and remove their default methods, making sure we swap their inheritance from
"MonoBehaviour" to "PlayerMovementState" as well. We now have our Initial States
so we can start caching them. To do that, head back to the
"PlayerMovementStateMachine" script. Remember that we'll only need to cache in
States that are not considered "Group States". This is because the player will never be
in the "Player Movement State" but instead on the "Idling State" or "Running State", as the "Movement State" simply
exists to group common logic. So, start by typing in
"public PlayerIdlingState IdlingState". I'll make it a property with an ommited set. In case you are wondering, here are the differences between writing "private set;"
or simply "omitting" the "set". If you don't want to think about it, simply make it "private" for all properties
that don't use a "public set". Duplicate this property 3 times and swap
their names to be "Walking", "Running" and "Sprinting". We now of course need to instantiate each one
of them, so create a constructor by typing in "public PlayerMovementStateMachine()" and inside simply type in
"IdlingState = new PlayerIdlingState();". Do the same thing for all of the other 3 States. Our States are now Cached. That's great, but you have likely
noticed we aren't yet calling any of the State methods in any "MonoBehaviour" class, which means their logic will never
run when we start playing the game. To do that, we need to first
create that "MonoBehaviour" class. So, back in Unity, go all the way back to the "Player" folder and create a new C# Script. I'll name it "Player". Opening it up, remove the default methods. All we need to do now is to create an instance
of our state machine and call its methods. To do that, create a new "private" variable of type "PlayerMovementStateMachine",
to which I'll name "movementStateMachine". We now have the "Constructor", "ChangeState", "HandleInput", "Update"
and "PhysicsUpdate" methods to call. We'll call them in the "Awake"
method, the "Start" method, the "Update" method
and the "FixedUpdate" method. In the "Awake" method we'll need to create
a new instance of our state machine, so type in "movementStateMachine =
new PlayerMovementStateMachine();". In the "Start" method we need to
define the player starting State. We'll start our Player in the "Idling State". To do that, type in "movementStateMachine.ChangeState();" and pass in "movementStateMachine.IdlingState". Our player should now enter the "Idling
State" whenever the game starts. In the "Update" method we'll need to handle our
input and run our non-physics related logic, so call in "movementStateMachine.HandleInput();"
and "movementStateMachine.Update();". The order is important as we want to make
sure we've got our "Input Data" updated before we do anything with
it in the "Update" method. In the "FixedUpdate" method we'll
need to run our physics related logic, so call in
"movementStateMachine.PhysicsUpdate();". We're now running our current State
logic through our Player Script. Of course, this will never happen unless we
add this script as a component of the Player, so head back into Unity, select the "Player" Game Object and add the "Player" script as a new component. If we now go ahead and enter play mode, a Debug.Log should be called saying that
the player is in the "Idling State".