Now that we have an understanding of the properties
and methods available when using Unity’s built-in character controller, we want to
get our character moving and tie that together with character animations! Not a problem! Today we are going to go through the full
process of adding movement and animation to our character! And we’ll do so using Unity’s new input
system! My name is Nicky and this channel is iHeartGameDev,
my channel all about game development! I’m so excited to announce that this video
is supported and made possible by all of YOU with the channel’s recently announced Patreon! Now, let’s get started! This project will be broken into three distinct
sections: setting up the animated character with its animation controller, setting up
the new Input system, and programming the movement with the animations using the new
Input System’s code! Feel free to skip around with the timestamps
and adjust the playback speed! And Patrons can now access today’s project
files directly through Patreon! Thank you so much for your support! Let’s start today’s project by heading
over to Mixamo.com and downloading 3 different animations! An idle animation, walking animation, and
run animation. We will go ahead and use our usual settings,
that have been previously explained in the Mixamo to Unity tutorial. We’ll also need to be sure to check the
in-place checkbox. Opening a brand new Unity project, we’ll
now import the packages that we need. In the package manager, we’ll select Unity
Registry and find, download and import the new input system. Once that’s complete, we’ll switch to
“MyAssets” and we’ll also download the Jammo Character model featured on MixAndJam. A link to Jammo on the asset store will be
in the description so you can add it to your assets too. Next let’s create an animations folder,
and then import our recently downloaded animations! And as shown, I’ve gone ahead and made a
small environment that will be great for testing our character movement! Now in order to use the imported animations
with other characters, we need to convert them to humanoid. Selecting all 3 of the animations, finding
the Rig Tab in the inspector, switching the animation type to “humanoid” and pressing
“Apply”, all 3 of these animations will be converted to “humanoid” animations. What this means is that all of the bones in
each animation have been remapped to the reusable humanoid structure of Unity’s mecanim animation
system. And We’ll also want to properly set up each
animation in the animations tab. Let’s first be sure that each animation
is properly titled. Occasionally, the animations from Mixamo are
titled “Mixamo.com” so we can name them accordingly and press apply. We’ll also want to be sure that “Loop
Time” is checked, as this is what has the animations repeat. Unity sometimes modifies the direction the
character is facing. To fix this, we’ll modify root transform
rotation, checking “bake into pose” and setting “based upon” to "original” for
the three animations. Great - animations are humanoid and ready
to be used! Let’s add Jammo to the scene! In the asset window, we’ll find the Jammo
model in the Models folder of the Jammo package. The Jammo model will need to be converted
to humanoid just like our animations. This will generate an avatar from the model,
storing the properly formatted bones and allow the model to use the humanoid animations. Now we can drag jammo into the scene and center
the character at the world origin. Just to test, we can press play and we’ll
see Jammo inanimate and just standing there. Let’s get Jammo animating! By default, models that have been converted
to humanoid will come the animator component already attached and the avatar inserted. As a refresher, this component is required
for animated gameobjects and hosts the animator controller that will control our three movement
animations. Next, In the animations folder found in the
project window, we’ll create a new animator controller by right clicking going to “create”
and selecting “animator controller”. We’ll title it “characterAnimationController”. We’ll then drag that into the reference
box of Jammo’s animator component. In play mode, Jammo still doesn’t animate
just yet. We need to add the animations to our new animator
controller. Double clicking on the animator controller
will open up the animator window. Here we can create our first animation state
by right clicking, selecting “create state” and “Empty”. We’ll title this animation state “idle”,
and then make two more titled “walk” and “run” as these will handle the respective
animations. We can change the default state to idle by
right clicking and selecting “setAsDefaultState”. We will then want to create transitions between
these three states by right clicking each, selecting “create transition” and connecting
them. Currently, we don’t have anything that will
cause these three animation states to transition from one to another. We need to create our animator parameters. For this tutorial, we just need two boolean
parameters: “isWalking” and “isRunning”. Once created, we’ll modify the transitions. Let’s disable “hasExitTime”, which will
ensure that the transition between each state is immediate. And then we’ll set our transition conditions:
“isWalking” needs to be true for the transition from the idle state to the walking state. And isRunning needs to be true for the transition
from walking to running. We can set the same transitions in the opposite
direction, but the required condition will be “false” instead. And lastly, we need to add the proper animations
to each animation state using their own reference boxes. Entering play mode with our character selected
in the hierarchy, we can now modify the isWalking and isRunning booleans in the animator and
see the results! Step Two in this tutorial is to set up our
project with the new input system. We’ve already downloaded the New Input System
package, so we are ready to get started. In our project window, we’ll right click,
go to create and select “Input Actions”. Let’s title it “Player Input”. This asset is how we can customize player
input for our entire game: from UI interaction to vehicle controls to character controls. Double clicking will open the Input Actions
window in the editor. Here there are three sections: Action Maps,
Actions and Properties. All three of which are interconnected. Action maps can be seen as collections of
actions. Each action being some type of input from
the player that will return data for us to use. And each action has properties, which are
modifiable in the properties section when actions are selected. This will be easier to understand with today’s
example! Pressing the plus button, we will title the
Action Map “CharacterControls”. Now, we can create actions that will be stored
within the CharacterControls action map. Let’s title this new action “Move”. Now that we have the action created, how do
we use it? When selected, we’ll see its current properties
divided into three submenus: Action, Interactions and Processors. The Action Type property allows us to choose
between “value”, “button” and “pass-through”. Each option determines what controls we can
possibly assign to the action, and ultimately what the return value for the action will
be in our code. For the move action we’ll select “value”
and we’ll then be presented with a “control type” with an additional dropdown with many
options. Typically character movement is handled with
“wasd” or a joystick and these values can both be stored within a vector2, so let’s
set that for the control type. Interactions will impact how we want the player
to use the action, for example: how long the button needs to be pressed to be triggered. But we won’t be needing these today. And “Processors” can be used to modify
the return value of an action. For today, we’ll need to use Normalize Vector
2. This will prevent a frequent issue where our
diagonal movement can be faster than our forward and side movement. This processor ensures that the max vector
length is 1. We’re now at the point where we can specify
what controls we want to tie our “Move” action to. Ideally we are able to use “WASD” or the
left joystick on a controller. The new input system makes this super easy
with “Bindings”. We’ll notice that when we created the “Move”
action, there was also an item right below it titled “No Binding”. Selecting it, we will see the properties window
change. Now we have the option to select the binding! Pressing on the binding dropdown, we can either
search for the input we’re looking for or we can use the “Listen” button which will
find any options dependent on the next button pressed! Oh and it’s worth noting that all of the
control options will be filtered to options that return vector2’s because of the “option
type” we chose earlier for this “Move” action. Now, if we connect a controller, we can press
“listen”, and move around the left stick. We’ll see and select the “left joystick”
option to set the binding. Awesome! We’ll also notice that the binding has it’s
own “interactions” and “processors” options which are specific to this binding. But again, there is no need for interactions
today and the processor is inherited from the action. With the left stick binding set up, we’re
almost there. How about we also get the binding for keyboard
controls: “wasd”. To do this, we’ll create a new binding underneath
our “move” action by pressing the plus button next to the action. Here we’ll find the option for the “Vector
2 Composite”. A “Vector 2 composite” allows us to map
any buttons we want to the four options available in a vector 2’s up down left right d-pad
configuration. For up, we’ll select and bind the “W”
key and go ahead and do the same for down left and right with the “S”, “A” and
“D” keys respectively. And that’s the process of setting up actions
and bindings! Let’s make one more one real quick. Pressing the plus button, we’ll title this
new action, “run”. For this, we’ll leave the action type as
a button! Buttons return boolean true or false values
depending on if the key or gamepad button is pressed or not. Let’s set up two bindings, one for keyboard:
“left shift”. And one for controller: “left bumper”. And just like that we have our “move”
and “run” actions set up! We’re just one last step away from being
able to use the new input system in our code. Back in the project window, we’ll press
on the Player Input Input Actions asset. When selected, we’ll see the option in the
inspector to “Generate C# Class”. Let’s check the box and press apply for
Unity to generate a script of our PlayerInput Input Action! This makes it much easier to access in our
code. Ok! We are ready to code! If you are brand new to coding, it’s important
to understand everything we type is case-sensitive and needs to be exactly the same to get the
same expected results. But don’t worry: with more experience you’ll
be able to modify and refactor where you see fit. We’ll try to be as beginner friendly as
possible, and this can serve as a solid starting point! To start, We’ll go ahead and attach a new
script to Jammo! Selecting Jammo and pressing on the “Add
Component”, we can type in “Script” and select “New Script”. Let’s title our script “AnimationAndMovementController”
and then double click to open the script in our code editor! Now, let’s start getting control of our
character over to the players. We’ll declare a new reference variable “playerInput”
of type “PlayerInput” inside the scope of the class. “PlayerInput” being the class that we
generated with the checkbox in the inspector. *This won’t work if that step was skipped* Now a declaration doesn’t do much. We’re basically just saying “this variable
that we’ve titled player input” is going to be this kind of class. We need to actually set this variable to an
instance of PlayerInput so that we can use it! Let’s set this variable inside of a new
function -- the “Awake” function. The Awake function is another life-cycle method,
like Start and Update, that is run even earlier in the life-cycle than the “Start” function! It’s the perfect place to set our reference
variables to instances, like we are doing here! And while we are at it, we won’t need the
“Start” Function today, so let’s just delete it! With this reference in place, we can now access
our action maps and actions from earlier! How can we properly do this? In this tutorial, the answer is “Callbacks”
functions. Instead, a “Callback” is set up to be
executed when certain events take place. For example: when a key or button is pressed,
or the analog stick is moved, then the callback function will be invoked. We will set up our callbacks in the Awake
function below where we just set the playerInput reference. We’ll start by writing “playerInput dot
characterControls dot move dot started”. This code navigates into our “CharacterControls”
action map, into the “move” action and says “ok Unity, listen for when the player
initially starts using this action”. Based on the bindings we set up earlier, would
occur either when the player presses the “W” “A” “S” or “D” keys or starts
to move the analog stick. Ok, next, we’ll add “plus equals ctx”
or “context”. This will essentially gives us access to the
current input data when the “started” callback occurs. And finally “equals greater than” and
some brackets. Inside of these brackets, we’ll be able
to use that context argument and start checking to see what keys are being pressed. Let’s give this a test: typing Debug dot
Log and passing in context dot readValue with “Vector2” as the argument we can show
the input in the editor console. We use Vector 2 because that’s the option
type we chose when setting up the “Move” action! Heading over to play mode and trying to press
our keys, we’ll immediately notice that it’s not working. But why? With the new input system, each “action
map” actually needs to be enabled and disabled. Back in our code, we can easily fix this using
MonoBehavior’s `onEnable` function. We’ll write “playerInput” dot “CharacterControls”
dot “Enable”. And we can do the same in an onDisable function
so that if this script is ever disabled, our game won’t continue listening for the player’s
input. Now in play mode, we’ll see that when we
press our WASD keys, the vector 2 will be logged to the console! W is 1 and S is -1 on the vector2’s Y value,
and A is -1 and D is 1 on the X value. With access to this vector2 that corresponds
to the player’s input, let’s put the data to use! We can go ahead and declare three new variables
in the scope of the class. A vector2 called “CurrentMovementInput”,
a Vector3 called currentMovement and a boolean “isMovementPressed”. Inside of the brackets of the callback, we’ll
write “currentMovementInput is equal to context dot read value vector 2”. Now, we are storing these values in our code! And immediately after, we are going to set
currentMovement’s x value equal to currentMovementInput’s x value. And currentMovement z to the currentMovementInput
y value. We assign to X and Z because the player controls
the movement on the x and z axis. As for the isMovementyPressed boolean, we
know that if the player is pressing any of the keys to move Jammo, the x or y value of
the vector 2 is going to be greater than or less than zero. So what we can do is set the isMovementPressed
boolean equal to that exact logic, writing “isMovementPressed is equal to currentMovementInput
dot x is not equal to zero or currentMovementInput dot y is not equal to zero”. Great! We have the current player input accessible
via our new variables. How do we get our character actually moving? Obviously, this series is covering Unity’s
built-in character controller. And after learning last episode about the
Move and Simple Move Functions, it’s time we actually used them.To do so, let’s head
back to the editor and attach the CharacterController component to Jammo. Be sure to re-center the collider to fit Jammo
properly With that, we can jump back into the code
and add a new variable. Keeping it simple, we will declare a variable
characterController of type characterController. And in the “Awake function” we will access
this component when Jammo is first initialized by setting “characterController” equal
to “Get Component” passing in the “CharacterController” class type as the argument. GetComponent will find the attached CharacterController
component! Ok! We have both the player input stored and access
to the character controller. We have what we need to start getting Jammo
moving! In the update function, we’ll write characterController.Move
passing in our constantly updated currentMovement variable! Entering play mode and pressing any of our
keys and we’ll see Jammo immediately fly off screen. Let’s take this back to our knowledge of
the Update function and our Move function. Update is called every single frame, so if
our game is running at 60 frames per second, it’s called 60 times. And in every call, we have our Move function
moving the distance of the Vector 3 we are passing in. So if we press forward and the Z value of
the Vector 3 is 1 point 0, then our character will move 60 units forward per second! To solve this issue, we can multiply our Vector
3 by Time.deltaTime. As a reminder, Time.deltaTime is the amount
of time since the last frame -- in other words a fraction of a second. Testing this new solution we’ll see our
character now slow down to a consistent and expected speed! Awesome! But wait, when we let go of the keys, they
Jammo isn’t stopping. What’s up with that? Let’s look at our callback. “Started” is specifically called when
the input system first receives the input, but that’s it! If we want to track when the key is let go,
we need to use the “Canceled” callback. So if we copy and paste our callback and all
of its logic, then rename “Started” to “Canceled”, we’ll now be able to see
when the player releases the key! This works great for keyboard, but controller’s
have a little more available motion. There are in-between values from 0 and 1. For this we can use one more callback: “Performed”. “Performed” will continue to update the
changes in the player input! Now rule #1 of programming is “Don’t Repeat
Yourself”, and here we have the same code being used 3 times. Let’s create a function that will handle
this logic! We’ll call it “onMovementInput”. It will accept an argument called context
of type InputAction.CallbackContext. And then inside of the function we can paste
the logic we had used in our callbacks. We will need to add the namespace “UnityEngine
dot InputSystem” for Unity to recognize the CallbackContext type. And now, instead of writing that logic three
times, we can simply pass this function! Back in play mode we will now see Jammo move
and stop as we continue to press the different keys… but, as we can see, movement and animation
are two different problems to solve. Jammo seems to be stuck in the idle state
despite the characterController moving. This is because we need to tell the animator
when we are walking and when we aren’t. Luckily, we already know that! So let’s grab access to the animator component
attached to Jammo and tell it the info! Below characterController, we can declare
a new variable “animator” of type “Animator”. And again, in the awake function we can set
Animator equal to get Component of type Animator. With access to the animator we can check and
update the animation states. Just like the character controller movement,
we are going to want to monitor and update the animation states every frame, so we can
do so inside the Update function. But if we aren’t careful, our code can start
to get really messy. Let’s write a new function that will handle
all of the animation state changes. We’ll call it “handleAnimation”, and
we can invoke it inside of the Update Function. At the top of this new function, we can check
to see the current values of the two boolean parameters we set up earlier: isWalking and
isRunning. We know that these parameters control the
transitions and switching of animation states. Using the Animator’s GetBool method and
passing in the stringified version of the parameter name, we can now locally store their
current values! And now we can set up some animation logic! To first get Jammo walking, we will write
an “if” conditional: If isMovementPressed is true, meaning a player is pressing in any
direction and the animator’s isWalking value is false, then we are going to want to set
the animator parameter to true so Jammo starts walking. We’ll set the value using animator’s “SetBool”
method. And then we’ll write another with the opposite
logic. If isWalking is true but the player is not
trying to Move Jammo, then we need to have Jammo stop walking. Testing this in play mode and… success! Jammo is now walking when we press forward! And he stops when we let go! However, it isn’t perfect. Jammo only walks in the direction that it’s
facing. How do we turn it? Well there are actually multiple ways this
can be done. For today’s tutorial, we are going to use
what’s known as a Quaternion. Quaternion’s are a type of number system
that we’ll find super helpful when dealing with rotations in 3d space, and Unity has
some awesome Quaternion functionality built in. Writing a new function “handleRotation”
we can declare three new variables: a Vector3 called PositionToLookAt, a Quaternion we’ll
call CurrentRotation, and a float titled “rotationFactorPerFrame” declared in the scope of the class. The positionToLookAt is essentially where
we are moving next. In other words, it’s our constantly updated
currentMovement variable except we are going to want to extract specifically the x and
z variables. Next we can set the currentRotation to transform.rotation. And the rotationFactorPerFrame, we will just
set to 1 for now. Below that, we will define a Quaternion called
target rotation, and we will set this equal to Quaternion dot Look Rotation passing in
our positionToLookAt variable. The built-in LookRotation method will create
a new rotation for us to use based on where our player is currently pressing. So let’s add that condition. Right below that, we will use another built-in
Quaternion method called Slerp, passing in the currentRotation from above, our new targetRotation
and the rotationFactorPerFrame. Slerp stands for spherical interpolation. What this will do is use the quaternions passed
as arguments and return a new Quaternion rotation based on the value of the third argument. The third argument should be a number between
0 and 1, and the closer it is to 1, the faster the spherical interpolation will be. Let’s add handleRotation to the update function
and test in play mode. So for our example where we have rotationFactorPerFrame
set to 1, it will immediately switch to the new direction. Of course, if we multiply by Time.deltaTime
when setting the variable, that number will become a fraction instead. And now it will now take multiple frames to
complete the full rotation! If we change 1 to a larger number, it will
increase the rotation speed! Now in play mode, Jammo is able to walk around
and will rotate accordingly! We have just a little more to go! Let’s get Jammo running! Having already created the “run action”
with the new input system, we can create a new callback that targets its values. We will write “playerInput dot characterControls
dot run dot started” and this time we will pass a new handler function called onRun. onRun will expect the callback context and
will update a new variable called isRunPressed in the scope of the class. And because we set the run action as a button
action type, we can use “callback context” built in “ReadValueAsButton” method to
set the isRunPressed boolean! And just like before, we need to know when
the button is let go, so we can duplicate this new callback and just change “started”
to “canceled”. Now, where can we use isRunPressed? We’ll first want to adjust the movement
speed depending on if the player is running or not. For this, we are going to need a new Vector3. We will title this last vector3 currentRunMovement,
and we will set its value the same as we are with currentMovement, except we will multiply
the x and the z values by a runMultiplier with a value of 3. We can then go back to the Update function
where we have our Move function being called and add the condition that if run is pressed,
then we invoke “Move” with the currentRunMovement, otherwise we keep the current implementation! Testing in play mode and Jammo now moves faster
when the run button is pressed! And finally we just need to add the running
animation states. But before we do this, let’s do a quick
refactor. In previous tutorials on this channel, we
learned about animator parameter hashing which is a performance optimization. So let’s go ahead and replace our string
versions with two new hash variables. We’ll declare them as isWalkingHash and
isRunningHash of type integer in the class scope. And then assign them in Awake using the animator
dot stringToHash function with string names we used before. Then we’ll replace the strings used in handleAnimation. Ok! Right below where we set the walking conditionals,
we can add two new conditionals for running. We’ll check if the player is moving and
that run is pressed, and that they are not already running. Inside this condition, we can set the parameter
to be true! And in our second conditional, we can check
if either the player stops moving or lets go of run, and that the parameter is currently
true. In this case, we turn off the run animation! In play mode, we will now see Jammo is able
to stand, walk, run and rotate! And because we have the built-in character
controller, we get all the benefits that it has to offer! The very last thing we’ll cover today is
gravity! Right now if Jammo walks off a ledge, it will
not fall. This is because we’ve dealt exclusively
with the x and z axes. For today’s project, all we need to be concerned
with is setting the y value of our currentMovement and currentRunMovement to a negative number. Let’s write a function, handleGravity, that
checks to see if the character is grounded or not. If the character is grounded, because of how
the built-in character controller works, we’ll still need to apply some downward force otherwise
our isGrounded parameter will switch between true and false. We can call this small downward value: groundedGravity
and set it to -.05. And if the character is not grounded we can
set the gravity equal to a much higher value, like Earth’s gravity! Now we’ll test in play mode and we will
see Jammo falling off ledges! Fantastic! Awesome job today! If you want to vote on the next tutorial on
this channel covering the Built-In character controller, you can check out the Ultra Supporter
tier on my Patreon! And when we have finished up this series on
the built-in character controller, you’ll decide what we learn next! As always, if you want to join and awesome
growing community, we would love to have you in the iHeartGameDev channel discord. The links are in the description! But that’s all for today, thank you so much
for watching and I’ll see you in the next video!