Our controller can now leap into the air and gracefully pirouette throughout the level. On the other hand, right now he might be a bit too acrobatic in the sky. Playing the game, you’ll notice that while jumping we retain a fine control over our movement direction. It’s very common in games to lose control of most (or even all) of your ability to change movement direction and velocity in jumping. This makes sense intuitively, as it is our legs contacting the ground that allow our bodies to accelerate and rotate. In this lesson, we will modify our controller to remove movement control in the sky.Before we sink our teeth into this delicious new feature, we should begin to do some cleanup in our code. You’ll notice each time we want to make use of the CharacterController, we need to call the GetComponent method. While this works fine, it’s a waste of resources to call this method over and over each frame, when the returned value never changes. Instead, we’ll cache the component in a variable. Add the following variable to the class:
private CharacterController controller;
This will store a reference to the CharacterController attached to our Player object. It won’t retrieve the component automatically; we’ll have to call GetComponent once to get the object. We’ll do this in the Start() method, which is called exactly once on every script. Add this line inside it:
controller = GetComponent<CharacterController>();
And now we have a handy reference to the CharacterController that we can use whenever we want. To make use of the variable, replace all the GetComponent calls with “controller”. For example, we would change the line checking the isGrounded property to the following
In addition to being more efficient with computational resources, it also makes the code much cleaner and easier to read.
Our next step is to remove the ability to move in-air, as well as ensure our controller maintains his forward velocity after jumping. Since this means that we’ll need to keep track of our character’s velocity at all times, let’s add a new variable to do so:
private Vector3 moveVelocity = Vector3.zero;
This will store our characters velocity on the Z-X plane, controlled by WASD. Next, let’s make sure that we can only move our character when our feet are on the ground. Remove the line that retrieves the keyboard input, and in the if statement that checks if the controller is grounded place this line:
moveVelocity = transform.TransformDirection(new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"))) * speed;
We’re not doing much new here—it’s simply a few lines of code we’ve written before compiled together into one, and then the result stored in moveVelocity. You’ll notice now that at any given frame, we are able to access our controller’s total velocity. Our keyboard movement is stored in moveVelocity, and our up-down in yVelocity. Let’s put them together using the following line:
Vector3 velocity = moveVelocity + yVelocity * Vector3.up;
Place this just above the final call to Move, and then replace the current controller.Move call with this:
controller.Move(velocity * Time.deltaTime);
…which is much simpler and far more concise! By building a velocity variable this way, we’re able to keep track (and modify) our player’s total movement vector at any time. Running the game will show the results of what we’ve done: our controller can now only move when he is grounded on the floor.
However, the overall movement feels a bit tight—on the ground, the controller is able to change direction on a dime, and having no movement in the air feels a bit too constraining. In a future lesson, we’ll once more modify our controls to add in smooth acceleration and deceleration, creating a better overall playing experience.