Character Movement in Unity 3D | New Input System + Root Motion Explained

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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!
Info
Channel: iHeartGameDev
Views: 158,640
Rating: undefined out of 5
Keywords: Character Movement in Unity 3D, Character Movement In Unity, iHeartGameDev, Character Movement Tutorial Unity, character movement in unity 3d, root motion tutorial, how to move characters in unity 3d, unity input system, animate in unity 3d, root motion tutorial unity, unity input system tutorial, new input system unity, movement tutorial unity 3d, movement unity, unity input system explained, unity root motion, root motion explained, unity3d movement, unity3d animation, unity3d
Id: IurqiqduMVQ
Channel Id: undefined
Length: 16min 49sec (1009 seconds)
Published: Sun Dec 27 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.