When moving an object using horizontal and vertical axes it’s possible to create diagonal movement that is faster than the speed of movement in a single direction.
This happens because the length of the vector that’s created from a separate forward vector of 1 and a sideways vector of 1 is longer than either one on their own, at around 1.4.
Technically, this can be shown using Pythagoras’ equation, where the length of the hypotenuse, that’s your diagonal movement, can be calculated using the sum of the other two sides, in this case the separate vertical and horizontal axes.
What this means in real terms is that holding forward and right on a keyboard, for example, can move the player 1.4 times faster than just moving forwards or sideways alone, causing faster diagonal movement.
So how can you fix it?
If you’re using Unity’s new Input System, then there are built-in options available to avoid this problem.
Simply choose a composite vector when creating your movement input, and choose either the Digital Normalized or Analogue mode.
However, if you’re creating a movement vector manually, such as combining the vertical and horizontal axes of the old Input Manager, you’ll need to adjust the length of the vector yourself.
One option is to normalise the movement vector.
This involves passing the horizontal and vertical axes into a single vector 3 value that is then normalised.
Like this:
Vector3 movement = new Vector3(x, 0, z).normalized;
However, while this does work, it means that the length of the vector is always one, meaning that any value less than one, such as analogue movements, or the gradual acceleration that Unity applies to digital axis inputs, is always rounded up to one, no matter what.
This might not be a problem for your game if, for example, you’re using the Raw Input values and are deliberately creating tight digital controls.
In this case the input is either on or off, so it doesn’t matter that gradual movement can’t be detected.
But, if you’re using the regular input axis values, which weight the value to create smooth movement input, you will probably want to translate this into your movement vector correctly.
To support vector lengths that are lower than 1, it’s possible to limit the length of the vector instead, using Clamp Magnitude.
Like this:
public float speed = 2;
void Update()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(x, 0, z);
movement = Vector3.ClampMagnitude(movement, 1);
transform.Translate(movement * speed * Time.deltaTime);
}
This will limit the length of the vector to one, while leaving lower values intact, allowing you to create analogue 8-way movement that’s even in all directions.