Unity’s new input system is out of preview
and it’s here to stay! This package simplifies how we can handle
multiple forms of player input! In this tutorial we are going to learn to
move animated characters using root motion and how to use the new input system to move
them with a gamepad! I’m Nicky, this channel is iHeartGameDev! Let’s get started! We’ll begin today by downloading an idle,
walk and run animations from Mixamo.com. If you want a thorough explanation of the
settings, check out my previous tutorial covering exporting from Mixamo. But the main difference today is that we will
leave the “in-place” checkbox unchecked! Opening a brand new Unity project, we’ll
add a plane to the scene along with a colorful material. Dragging our downloaded FBX files into the
project, we will convert our characters to humanoid, and then duplicate the animations
attached to each file. We will select the “loop time” checkbox
for each duplicate animation so that the animation will continuously repeat. Cool. Next, let’s drag our idle fbx file into
the scene hierarchy to and the character to the scene. Now, to get our character moving using root
motion, we are going to need to add components directly to this character. Because we converted our character to humanoid,
an Animator component has already been added and the character’s avatar was generated
and applied. The avatar is Unity’s remapped bone-hierarchy
and it automatically assigns the root-bone for root-motion and gives us access to tools
like animation retargeting. Pressing on “Add Component” we will type
“New Script” and name the script “characterMovement”. We should now have an animator component and
script attached to the character. Let’s open our project tab and right click
to create a new animator controller titled “movementController” and drag it into
the animator’s empty reference box. Double clicking on the animator controller,
we’ll open the animator window. Let’s add our downloaded animations by dragging
in the duplicate copies starting with the idle animation. The idle animation should be highlighted in
orange, meaning that it’s the default animation state. In play mode, we will see that the idle animation
will now play and repeat over and over again because we have the loop time checkbox active. How do we get the character moving? Simple! We can set up transitions between our animations! And each transition will be controlled by
parameters. Let’s create these now. We’ll create two true/false boolean parameters:
“isWalking” and “isRunning”. Then we’ll add transitions from the idle
state to the walking state and the walking state back to the idle state, each being controlled
by the “isWalking” parameter. We’ll set the condition to be “true”
for the transition from the idle to the walking state and “false” for the transition from
the walking state to the idle state. We’ll toggle off “has Exit Time” so
the animations will transition when the parameters change.” We can repeat this from the walking state
to the running state, except we’ll use the isRunning boolean for the parameter controlling
these transitions. Testing our work in play mode, we’ll see
some unexpected results when we activate the “isWalking” boolean. Our character will begin walking but not in
a straight line. This is where we begin to talk about Root
Motion. My understanding of root motion is that the
character’s movement in worldspace is “baked” or “recorded” into the animation. What this means is that the character as a
whole is moving in the X, Y or Z axis. Applying root motion can be as easy as checking
a box, as is required with the animator component’s “Apply root motion” checkbox. But sometimes we need to modify the animation
for our desired results. Selecting our Run FBX, we’ll have access
to settings that begin with “Root Transform”. Let’s take a look at each sub-setting! Bake Into Pose essentially means that the
game object will not be impacted by the animation: be it the rotation, the height or the changes
on the X/Z axis. The green/red indicator is whether Unity recommends
that we use the Bake Into Pose option or not. “Based upon” lets us choose the orientation
of the clip. “Body Orientation” and “center of mass”
are typically default settings as Unity calculates these values. These, respectively, will adjust the clip
to face the forward vector of the body or stay in line with the chosen root transform. We can also manually apply an Offset to each
and adjust the direction ourselves. And “Original” will just use the offset
recorded from the imported animation clip. So to fix our running issue, we just need
to switch to “Original” for root rotation and apply bake into pose! Ok, we are at the point where we need to grant
the player control of our character. Let’s take a look at the New Input system! We will need to first install the package. No big deal. Let’s press “window”, “package manager”,
and find and select “Input System”. We can install the package and, when prompted,
we can restart Unity to disable the old input system and activate the new one! We will start using this new input system
by creating a new InputAction asset in the project tab. Right clicking in the project window, we will
go to “Create” and select “Input Actions” towards the bottom. We can rename this asset to “PlayerInput”. Double clicking on the asset, a completely
new input actions window will open. This is the new interface to handle player
input. In this window, we’ll see three sections:
Action Maps, Actions and Properties; all of which are interconnected. Action maps can be seen as collections or
containers for a list of actions. Each action is some type of input from the
player. And the properties section is where we designate
additional details about the input. Let’s create our first Action map as an
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 “Movement”. When creating a new Action, Unity will also
generate an empty binding associated with it. A binding is the “player input” that we
get to select for an Action. In our example, “movement”, we will select
the left joystick. The Properties Section references whatever
is actively selected in the Actions section. If we press on the “Movement” Action,
the properties panel will display 3 menus: Action, Interactions, and Processors. Action has a single dropdown menu titled:
“Action Type”. Here we have three options: Button, Value,
or Pass-Through. Value is the most robust of the action types. When selected, we get a new dropdown menu
titled “Control Type”. And here we have a large list of all possible
input types that a player can perform. Selecting a control type will limit the type
of bindings displayed. The “Button” action type will only permit
us to use “ButtonControl” inputs, like keys on a keyboard or the face buttons on
a controller. And Pass-Through has the same input options
as Value, except Pass-Through is missing a feature called “Disambiguation”. Disambiguation essentially chooses which controller
is in control if there are more than one connected at a time and it does so by measuring which
has the largest changes occurring. We want our character to be controlled by
the left stick so let’s select “Pass-Through” or “Value” as the Action-Type and we actually
have a few options for control type, mainly being “Vector 2” or “Stick”. Let’s go with “Vector 2”. When we press on our empty Binding, we see
in the “Properties” menu that our “Action” submenu switches to: “Binding”. Binding has an empty selector titled Path,
which is the input. What’s cool about Path is we can either
type in the name of the input we are looking for, like “Joystick”, or we have this
“Listen” button which, when pressed, will save any inputs from a device and list them
as options -- making it faster to find the exact input we are looking for. For today, we are going to be using a Nintendo
Switch Pro controller which is simply connected via bluetooth. But this also works with PS4 and Xbox Controllers
and more. Now, Unity should automatically detect the
controller. To be sure this is the case, we can go to
Window, Analysis, and Input Debugger. In the new window, we should see our controller
and we should see a constant flow of input detection. Once confirmed, we can use the “Listen”
button and just spin the left stick around to find the “Left Stick” Input Binding. This binding is a blanket input binding for
all supported gamepads, rather than being specific to the Pro Controller or the PS4
controller. Let’s add our only processor for today:
stick deadzone. This will set any input value that’s less
than the minimum to 0, and set any value greater than the max to 1! And we’ll cover the rest of the processors
in the future! Let’s create one more Action, title it “Run”,
set it as a “Button” and then map it to the left shoulder button. We’ll add the “press” interaction, and
select “press and release” from the dropdown. This will trigger the action on the press
and on the release which will be helpful in our code. We’ll have our player hold left bumper when
they want to run. Great! The last things we need to do with our input
is save and create a class. Let’s select our input asset and take a
look at the inspector. Here there is a checkbox titled “Generate
C# class”. Let’s check this and hit apply. This will make it easier for us to access
our actions in our code. Entering play mode, we’ll see that what
we’ve done thus far with our new input system has not impacted our character at all. Let’s open our script and get our character
moving! Let’s begin by declaring a handful of variables. Let’s start with what we are familiar with
from previous tutorials, declaring an animator variable of type “Animator”, and two integer
variables titled isWalkingHash and isRunningHash. In our start function, we can set the animator
to the Animator component using the GetComponent function and passing in Animator as the parameter,
and right below that we can set our two hash variables using the Animator.StringToHash
function and passing in their respective parameter names -- be sure to use your own parameter
titles if you titled them anything different. Ok cool! Next we’ll start to set up our movement
logic. In a new function titled “handleMovement”,
we will create two new local boolean variables, isWalking and isRunning. We’ll set these equal to the result of animator.GetBool,
passing in each respective hash as a parameter. At this point we can begin coding logic using
the new Input System. Scrolling to the top of the file, we have
the default Namespace imports which are signified by the “using” directive. Basically what this does is allow us to access
other classes, like the InputSystem! So we will add “using UnityEngine.InputSystem”! With this we can now access the C# class that
we generated when we applied the checkbox from the inspector of our input asset. Directly below our declared hash variables,
we can add a variable “input” of type “PlayerInput”. Note, this should be whatever you titled your
input asset because that’s what the title of your C# generated class will be. Next we will set our input variable, but we
will do so in a new function. The awake function is similar to Start, but
it’s called even earlier: when the object that the script is attached to is initialized,
not when it’s enabled. Here we will set input equal to a new instance
of PlayerInput, which is the class we created for our input actions when checking the box
in the inspector. Doing so allows us to directly access our
Action Map and Actions! Now, in programming we have what are called
“Listeners” which means that our code is waiting for some type of event to occur. In our case, the actions we set up earlier
wait for a joystick to be moved and wait for the left shoulder button to be pressed. When either of these happen, our code will
respond! And they will respond by providing accurate
controller values to what is known as a callback function! Let’s set up these callback functions. Now that we have our input variable set in
Awake, we can access our action maps and actions! Typing input.CharacterControls.Movement we
are now accessing our movement action. This action has a list of functions available
to it. We will use “performed” by adding .performed. “Performed” is a callback function, and
it will return the current context of the player input. Current context contains information on the
current state of the callback. So in our case, the current context is a result
of movement.performed, and will therefore have information on our joystick. Code syntax for callbacks in C# is += ctx
(for context) and then what is known as a lambda expression, which looks like =>. On
the right side of the lambda expression is the lambda body, which is where we can use
our context! Ok great, so we know that the current context
has data on our joystick! How can we access that data? Like our movement Action, current context
has functions built in and available for us to use! Now if we don’t already know the type of
value the context is holding, we can use the context’s built in “ReadValueAsObject”
function and log the value to the console. Before we test our current logic in play mode,
we need to actually enable the ActionMap. We would want to do this when our character
is enabled, so we just need to add input.Character.Enable() to an OnEnable function. We can do the reverse with Disable. Entering play mode we can see it’s a vector
2! Great! And if we do the same with the current context
of our “run” Action, we can see it’s a float! Knowing these types, we can store and use
them to get our character moving through player input! We will add three new variables at the top
of the file: a vector2 titled currentMovement, and two booleans titled movementPressed and
runPressed. Back to our callbacks, we can now set our
new movement variable equal to context.readValue passing in vector2 as the parameter. However for our run callback, we want the
value to return as true or false whether the button is pressed or not. Instead of using “readValue”, we can use
“readValueAsButton” instead! By default, “readValueAsButton” will return
true if the float value is greater than 0, and false when it’s zero! And then for our movementPressed boolean,
we just need to see if either the x or the y value in current Movement is greater than
0! Ok! At this point we have locally stored values
for both of our inputs with the new input system! Let’s get our character moving by going
back to our handleMovement function. We only want our character to start walking
if movementPressed is true but our isWalking animator parameter is false. And we want to reverse that logic to stop
walking. For both conditions, we’ll use the animator.SetBool
function, pass in the isWalking Hash and the appropriate boolean to change the animator
parameter value. We can apply similar logic for running, but
this time checking to see if both stored player input values, movementPressed and runPressed,
are true or either are false. Before we test our current logic in play mode,
we need to invoke handleMovement in our update function! Now, let’s test our current progress. Entering play mode and tilting our joystick
forward, our character starts walking, pressing the left bumper our character runs, and letting
go of the stick it will completely stop. Rad! And we can make this even better. Right now, our character only moves in one
direction. But we can fix that with the values of our
currentMovement vector2 and a bit of code! Back in our script, we will write a new function
called handleRotation. Our currentMovement variable stores the joystick
direction as normalized values between -1 and 1 for both x and y in the vector2. We can use these values to rotate our character
to point in the right direction! We’ll start by grabbing the current position
of the character as a Vector3 by using transform.position. Next we’ll create a new Vector3 with the
two values that we already have stored in currentMovement. This is because the world has an x, y, and
z axis and that is how our character’s position is stored. We’ll say “Vector3 newPosition” is equal
to new Vector3 passing in the currentMovement X value for x, skipping y, and using the currentMovement
Y value for z. And the last Vector3 we need is what we will
call the positionToLookAt, which is the current position and the new position added together. We can now use Unity’s built in function
transform.LookAt and pass in this combined position! LookAt will rotate the transform so that its
forward direction is pointed at the position passed in! And the last thing we need to do is invoke
handle rotation in our update function! Pressing play and using our controller, our
character will run around and it will follow the movement of our joystick! We did it! And with a little bit of modification, we
can have some isometric gameplay! Great work today! If you want to support the channel, please
consider liking the video and subscribing to the channel! It makes a big difference in helping awesome
developers like yourself find this channel! In the future, we’ll take this knowledge
and apply it to 360 degree movement! We’ll also start using rigidbodies and character
controllers! As always, you’re invited to join our awesome
growing channel discord and to follow me on twitter for channel updates! But that’s all for today, thank you so much
for watching, I’ll see you in the next video!