Moving in a Straight Line in Unity 3D

Physics in Unity are weird.

When pressing an arrow (or WASD) key we expect to see the character moving in a straight line. But under the gravity’s effect the engine computes the movement as a parabola instead, resulting in this deviation:

unity straight line Moving in a Straight Line in Unity 3D

But before we start solving the problem lets take a look at the initial code.

// User’s input
private Vector3 input;

// Player’s spawn point
private Vector3 spawn;

// Player’s movement speed
public float playerSpeed;

// Maximum speed the player can reach
public float maxSpeed;

// Player’s falling death distance
public float deathDist;

// Use this for initialization
void Start () {

// Stores the Player’s starting position
spawn = transform.position;

}

// Update is called once per frame
void Update () {

// Stores the commands
input = new Vector3 (Input.GetAxisRaw(“Horizontal”), 0, Input.GetAxisRaw(“Vertical”));

// Checks if the player hasn’t reached max speed
if(rigidbody.velocity.magnitude < maxSpeed){

// Moves the player
rigidbody.AddForce(input * playerSpeed);

}

// Checks if the player fell off the map
if(transform.position.y < - deathDist){

// Stops the movement
rigidbody.velocity = Vector3.zero;

// Positions the player at the starting point
transform.position = spawn;

}

}

Basically what this does is taking the input defined in the Input Manager (Edit > Project Settings > Input) and applying it as a force to the character’s rigid body. And if the player falls off the platform the character goes back to the starting point.

So how can we fix this?
Well since the problem is the physics engine, then we need to calculate around it and apply the movement to the character itself, not to the rigid body component the engine uses to control the motion.

// User’s input
private Vector3 input;

// Player’s spawn point
private Vector3 spawn;

// Player’s movement speed
public float playerSpeed;

// Maximum speed the player can reach
public float maxSpeed;

// Speed counter (acceleration)
private Vector3 speed = Vector3.zero;

// Friction
public float friction;

// Player’s falling death distance
public float deathDist;

// Use this for initialization
void Start () {

// Stores the Player’s starting position
spawn = transform.position;

}

// Update is called once per frame
void Update () {

// Stores the commands
input = new Vector3 (Input.GetAxisRaw(“Horizontal”), 0, Input.GetAxisRaw(“Vertical”));

// Checks if the player hasn’t reached max speed
if(rigidbody.velocity.magnitude < maxSpeed){
// Moves the player
rigidbody.AddForce(input * playerSpeed);

}

// Checks if the player hasn’t reached max speed
if(speed.magnitude < maxSpeed){
// Increases the speed
speed += input * Time.deltaTime;

}

// Decreases the speed
speed /= friction;

// Moves the player
transform.position += speed;

// Checks if the player fell off the map
if(transform.position.y < - deathDist){

// Stops the movement
rigidbody.velocity = Vector3.zero;
// Stops the movement
speed = Vector3.zero;

// Positions the player at the starting point
transform.position = spawn;

}

}

This is how the solution looks after removing the old code (red) and adding the new one (green).

What’s happening here is we’re performing all the calculations on a vector named speed and then adding it to the character’s position in order to move it. Specifically taking the player’s input and incrementing the speed’s value, but applying later a friction value to decrease the said speed.

And this is the result:

unity straight line2 Moving in a Straight Line in Unity 3D

*special thanks to João Alves for helping me with this*

You may also like...