Common Gamepad Input Mistake Caused by Vector Normalization - Unity Game Development Tutorial

In this Unity game development tutorial we're going to at how to solve a common mistake that's often made when processing input from a gamepad.

You can either watch the video version below or continue reading for written instructions.

Right, we're going to start with the project we created in our 'Character Rotation' video. In this video we created a character that can move around our scene based on input from the keyboard or the gamepad.

The problem is that the character always moves at the same speed no matter how far we move the gamepad thumbstick. There is no difference between moving the thumbstick all the way in a direction and moving it a fraction. 

This probably isn't what we want. In most games, the character's speed will reflect the thumbstick movement. For example, the character may walk slowly when the thumbstick is moved a fraction and run when it is moved all the way.

Right, let's look at our script to see where we're going wrong.

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float speed;
    public float rotationAnglePerSecond;

    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        Vector3 movementDirection = new Vector3(horizontalInput, 0, verticalInput);
        movementDirection.Normalize();

        transform.Translate(movementDirection speed * Time.deltaTime, Space.World);
        
        if (movementDirection != Vector3.zero)
        {
            Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);
            transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationAnglePerSecond * Time.deltaTime);
        }
    }
}

We're getting the input from the horizontal axis and the vertical axis. These input values will range from -1 to 1 and will correctly reflect the movement of the thumbstick. For example, if the thumbstick is halfway to the right, the value of the horizontal input will be 0.5.

We then create a vector from these input values for our movement direction. Then we normalize this vector before moving the character in this direction.

The problem is actually introduced in the line where we normalize the vector.

To understand the problem let's look at what actually happens when we normalize a vector. 

If our vector is 1 on the X and Z axis then the magnitude of our vector is 1.414.

Diagram showing the magnitude of the vector before normalization

Normalising the vector maintains the direction but sets the magnitude to 1. So, in this scenario normalising our vector will reduce the size on the X and Z axis to 0.7, making the magnitude 1 while maintaining the direction.

Diagram showing the vector dimensions after normalization

In this scenario, the normalisation gives us what we want and ensures our direction vector doesn't have a magnitude greater than 1.

Now let's look at this scenario. 

Diagram showing a smaller vector with a magnitude less than one

If our vector is 0.5 on the X and Z axis then the magnitude of our vector would be 0.707

If we normalize this vector it will increase the size on the X and Z axis to 0.7.

Diagram showing the vector dimensions after normalization

This is what's happening with our gamepad input. No matter how small the thumbstick is, the normalisation will increase the magnitude to 1.

Let's go back to the code and make the following changes to fix this.

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float speed;
    public float rotationAnglePerSecond;

    void Update()
    {
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        Vector3 movementDirection = new Vector3(horizontalInput, 0, verticalInput);
        float magnitude = Mathf.Clamp01(movementDirection.magnitude);
        movementDirection.Normalize();

        transform.Translate(movementDirection * magnitude * speed * Time.deltaTime, Space.World);
        
        if (movementDirection != Vector3.zero)
        {
            Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);
            transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationAnglePerSecond * Time.deltaTime);            
        }
    }
}

We still want to normalize the vector, as a direction vector should have a magnitude of 1. But before we do we're storing the magnitude. We then multiply by this magnitude when moving our character.

Also, to limit the magnitude to a range of 0 to 1 we are using the Clamp01 method.

Let's save the game and switch back to Unity.

We'll press play to try this out.

The character moving slow and then fast

Now when we partially move the thumbstick in a direction our character moves slowly, and when we move the thumbstick all the way it moves at full speed.

That covers everything for this tutorial. We hope that you found it useful. Please leave any questions or feedback in the comments below, and don't forget to subscribe to get notified when we publish our next post.

Thanks.

Comments

  1. Why normalise it then multiply by the clamped magnitude? Why not just use the clamped value without normalisation?

    ReplyDelete
  2. This Unity tutorial swiftly corrects gamepad
    input issues, enhancing character movement. Your concise code adjustments and interactive Unity demonstrations greatly improve clarity. Any plans for more tutorials? Appreciate the helpful content!





    ReplyDelete

Post a Comment

Popular posts from this blog

Rotating a Character in the Direction of Movement - Unity Game Development Tutorial

Changing the Colour of a Material - Unity Game Development Tutorial

Creating Terrain from Heightmaps - Unity Game Development Tutorial