Gravity, Jumping and — why not — Double Jumping
Objective: implement a custom simple physics to create gravity and the ability to jump
As I promised, it’s time to make our own gravity. Let’s revise some basic physics first. Or skip to the next paragraph!
Little dissertation on Weight
Everything having a mass is subject to gravitational attraction towards the centre of the Earth. Well, not exactly the centre, since the earth spins (day/night cycle) it is a non-inertial reference frame, so the bodies are not only subject to gravitational force but also to a set of other so-called fictitious forces or, more properly, inertial forces such as Coriolis Force, Centrifugal Force…
When considering also this forces, the body appears to be attracted not towards the centre of the planet anymore, but towards a point near to it. Also the magnitude of the force is affected. This force is more properly called Weight, and is expressed in the famous formula
This new force applies along a new direction which defines, univocally, what we may call a Vertical direction, being Horizontal every other direction perpendicular to the Vertical one (an entire plane). Since we are little tiny ants if compared to the planet, we may assume that around us (in a reasonably large area) this Vertical direction doesn’t change sensibly, and so do the magnitude of the force: for a given mass it is constant — that’s why buildings appear box- and not wedge-shaped.
g is the acceleration of a body subject to only its weight, and since it is constant, a falling body will move in the so-called motion with constant acceleration which laws are (courtesy of wikipedia)
where g is the gravity acceleration constant, v and y represent the initial (at t=0) values of velocity and position. What we can notice is that the velocity grows linearly with time, while the position has a quadratic law. This info is vital for our in-game implementations.
Right now, without any physics taking place in our platform, we are able to move the character along x axis, even if there is no platform under its feet!
Let’s take into account gravity. We are using the
Move(vector3) method, which accept a displacement in form of a vector3. The displacement has been calculated as
velocity * Time.deltTime . We can do it also for our falling movement.
As Galileo Galilei once stated: motions combine, so we can add the falling motion to the velocity vector and make a single
It may be confusing, because this is the final result, but it will be clear in a moment. Gravity, or weight as force applies always to the body. Always. Even if it is blocked by a floor and thus it is still. The difference between this case and a free fall is that the floor counteracts balancing the weight: no total force applies to the body. In our simple world we are not simulating forces but just applying the kinematics.
The character controller already stops when in contact with a collider, but this doesn’t automatically happens to our calculations. It’s our job to stop it.
The character controller property
isGrounded comes handy: it is true if the character touches something below, and false otherwise. We can simply say: ok, if it’s grounded then the y-component of velocity vector is zero, else it is calculated using the quadratic law (
time.deltaTime is multiplied twice).
Easy! But why do we need to put the velocity to zero, since the character already stops on a floor? The answer is simple: if we do not clear the velocity, it will continue to grow even if the character is not falling, meaning that in the very moment there will be no floor under its feet… it will go down incredibly fast instead of start its fall as expected!
There is still a problem, and it’s not our fault. If you log the
isGrounded property you’ll see it’s flickering while the character is on a ground, it just won’t stop on true value! There is an explanation though: when the character is grounded it is in “contact” with the ground, but Unity is not really consistent when it comes to “contact”. Actually, the character controller is trying to penetrate the ground which, in return, pushes it out. The result of this loop is that it will try to fall through the floor going up and down. The range of motion is actually very little and it occurs really fast so it is difficult to tell.
A simple way to overcome this problem is to put the y velocity to a small, negative value, as if we were just pushing the character to floor. Since the value is small, it will not affect the fall (when we start adding velocity again), but since it is not zero, at last we stuck the character to the ground!
If you’re wandering what value to use for
g , well real world value is
9.81 but you’ll find it odd in game: I think that we are used to Mario-like or Sonic-like character much quickly moving, jumping and falling. So try and find your best match!
Let’s now introduce jumping. Jumping is nothing more that adding positive y-velocity to the character.
Again, values are a choice of yours, but let’s see what’s happening here. If the character is grounded, and if we press the space bar the y-velocity changes. Why setting and not adding? I’m thinking of the possibility to implement a double-jump: while in air we can jump again. But if we press the jump key during falling, when velocity is negative, simply adding a value would not have the desired effect of making the character really jump! So we set again the value to a positive one.
As Van Halen would say: Go ahead and jump!
This feature will allow the character to jump again while in air but at this conditions:
- The player can double-jump just once per jump
- The player has to jump in order to double-jump (no jumping when just falling)
To achieve this behaviour we need just a simple change in the code.
We start with a bool
_canDoubleJump = false . If grounded, the player can jump, once jumping the bool inverts its value (thus becoming true). While in air there is a check on this bool: if true, another call on
Jump() can be performed and, if done, the bool inverts again (false) making impossible to jump again while still in air.
When the player is grounded again, the bool is set to false but it is possible to jump again without checks and so on.
Since Mario World is, in my humble opinion, the masterpiece reference when talking about platform games I tried to figure out which values to use in order to have a similar gravity behaviour. Looking on the internet I found this interesting article about this topic and found out that Mario was subject to an extreme force!
If you’re looking for a similar feeling and don’t want to switch to a constant-velocity gravity these are the (crazy) values:
g = 60
jump speed = 17