Menu
CoddyTech

Gravity by Hand

Part of the Building Real Games section of Coddy's Unity journey. Lesson 38 of 56.

A CharacterController falls only if your script makes it fall, and a real fall speeds up: gravity adds about 9.81 units a second to the falling speed every second. So the script keeps the vertical speed in a field, adds gravity * Time.deltaTime to it every frame, and moves by it in the same Move as the walk:

public float gravity = -9.81f;
float verticalSpeed;

void Update()
{
    if (controller.isGrounded && verticalSpeed < 0f)
    {
        verticalSpeed = -2f;   // on the ground: end the fall, keep a light push down
    }
    verticalSpeed += gravity * Time.deltaTime;

    Vector3 move = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")) * speed;
    move.y = verticalSpeed;
    controller.Move(move * Time.deltaTime);
}

Time.deltaTime appears twice for two jobs: gravity * Time.deltaTime is the speed gained this frame, and the speed times Time.deltaTime is the distance moved this frame. The speed is a field because it has to last from frame to frame; a local variable would start again at 0 every frame. Falling from rest, the controller is at -4.9 units a second after half a second and -9.81 after one.

The reset matters as much as the fall. On the floor Move stops the controller, but nothing stops the field: without the reset it keeps growing while the player stands, and the next step off a ledge drops at full speed. It is reset to a small negative number rather than 0 so that every Move still presses into the floor: at a high frame rate the push of a single frame's gravity can be shorter than Min Move Distance, the Move does nothing and isGrounded turns false. isGrounded is read before the Move, so it describes the last frame's Move.

challenge icon

Challenge

Medium

The diver stands on a tall tower, and the arrows walk it (already written). Give it gravity in Diver.cs. The Inspector sets gravity (-12) and maxFall (10), and the script has the field verticalSpeed.

  • Every frame, verticalSpeed gains gravity per second, and the diver moves down by it in the same Move as the walk.
  • Air slows a long fall: the diver never falls faster than maxFall units a second.
  • When the controller is grounded and verticalSpeed is below 0, set verticalSpeed to -1.

Press Play, walk off the tower, then Check.

Try it yourself

using UnityEngine;

public class Diver : MonoBehaviour
{
    public float speed = 4f;
    public float gravity = -12f;
    public float maxFall = 10f;

    CharacterController controller;
    float verticalSpeed;

    void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        // Gravity: reset verticalSpeed on the ground, add gravity, limit the fall, put it in move.y

        Vector3 move = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")) * speed;
        controller.Move(move * Time.deltaTime);
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Building Real Games