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
MediumThe 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,
verticalSpeedgainsgravityper 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
maxFallunits a second. - When the controller is grounded and
verticalSpeedis below 0, setverticalSpeedto -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);
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Building Real Games
2Buttons and Screens
Buttons and onClickLambdasWiring in the InspectorPanels On and OffHealth BarsRecap: Upgrade Shop5Saving with PlayerPrefs
Save a NumberLoad with a DefaultHigh ScoreSave SettingsRecap: Coins in the Bank8Character Controller
CharacterController.MoveGravity by HandJumpingPushing ThingsMove Where You LookRecap: Double Jump3The Game Manager
One Script Runs the GameThe SingletonGame StatesSwitching on the StateRecap: Round Manager6Project: Endless Runner
Three LanesObstacles Ahead9The New Input System
Two Input SystemsKeyboard and MousePressed This FrameInput ActionsRecap: Input Swap