◀ 5.36.16.2 ▶

6.1 — Gravity & Jump: The First Flick

The cave is the first place in this game where down means something. Walk off the edge of the floor and you do not fall — you keep walking, level. Today you turn gravity back on for one scene, and use the same body to throw the character into the air.

In this lesson you will be able to answer“how do I jump?”Full walkthrough
Break It Down~10 minFull walkthrough

The character falls, and then it jumps. "Make the player jump" is really two mechanisms wearing one name: a world that pulls you down, and one instant where the body is handed an upward velocity that the pulling-down then spends. Neither half exists in the cave today, and the same prefab must keep NOT falling in the world you built it for.

First, before any code: cut that into parts small enough that you could look each one up on its own.

  1. Give the Player a body that falls in this sceneBody Type Dynamic and Gravity Scale above zero - it takes both. Gravity Scale multiplies the project gravity Physics 2D has at -9.81 on Y. Your body has been Dynamic with Gravity Scale 0 since 2.1.Read: the rb.gravityScale row of the quick reference.
  2. Tell the script which world it woke up inThe same prefab is played in both worlds, so "am I in the cave?" is a fact about the scene: one serialized bool, ticked on the cave copy and left alone on the Overworld copy - the hasAxe shape from 5.1, pointed at the world.Read: the [SerializeField] private bool row in the quick reference.
  3. Turn gravity on once, when the scene loadsA gated write in Start(): if the flag is on, hand the body a gravity scale. Once, before the first frame - in Update() the same number is written sixty times a second.Read: the void Start() row of the quick reference.
  4. Notice one press of the jump keyInput.GetKeyDown(KeyCode.Space) inside the Update() you have been adding to since 1.4. True on the single frame the key goes down; GetKey is true on every frame it is held - a jump is a moment, not a state.Read: the Input.GetKeyDown(KeyCode.Space) row of the quick reference.
  5. Hand the body one upward flick, and let gravity spend itAddForce(Vector2.up * jumpForce, ForceMode2D.Impulse): one instant change in momentum. The height is the impulse divided by mass, the rise is paid for by the gravity from part 1, and the apex is the frame the vertical velocity reaches zero.Read: the rb.AddForce(... ForceMode2D.Impulse) row of the quick reference.

Three of those five parts are work you have already done: a Dynamic body is 2.1, a serialized slot is 1.3 and 5.1, a once-only write in Start() is 3.3. The other two have no precedent, which is why this lesson walks every line.

The test: could you look this part up on its own, right now, in one search? "Make the player jump" cannot be looked up - there is no such API. "Push a rigidbody upward once" can.

Your turn

The flag in part 2 is the first thing you have ever stored about the world rather than about the player. Name one other fact about the scene in your game, and say where you would keep it.

Part 4 notices a key press; part 5 moves the body. Nothing in the file measures time, and yet the arc has a height. Where does the height come from - and which part would you change first if the jump felt wrong?

In the Editor ~20 min
  1. Project window → open Assets/_Scenes/Platformer.unity — the cave — then Game view → press Play and walk off the edge of the floor. Nothing falls, and that is not a bug: the Rigidbody2D you gave the Player in 2.1 has Gravity Scale 0, because a top-down character who obeys gravity falls through the world forever. You were right then. The cave is the first place where you are wrong.
  2. Hierarchy → select the Player, then Inspector → Rigidbody2D → Gravity Scale. Set it to 1 while the game is running and watch the character drop. Then try 0.2 and describe the difference out loud.
  3. Game view → stop Play, then Inspector → Gravity Scale: it is back to 0. Inspector edits made during Play are thrown away on Stop (1.2) — which is why the cave's gravity has to end up in code.
  4. Hierarchy → open the Overworld scene — the top-down hub — select its Player, and set Inspector → Gravity Scale to 1. Game view → press Play: you have just broken the hub. Write down what it cost, then set it back to 0.
  5. Hierarchy → back in the cave, select the Player and read Inspector → Player Controller. There is a leftover override called Is Platformer that no field in your script matches yet. Leave it — step 1 of the walkthrough below is about it.
  6. Once the walkthrough is done — Hierarchy → the Player standing in the cave, Inspector → Player Controller → Is Platformer, and tick it. Confirm the Overworld's copy is unticked, then Game view → press Play in both: the cave falls, the hub does not.
In the Code ~30 min

Two lookups and two decisions: what the Rigidbody2D offers, and the four ways a body can leave the ground.

Field on the Rigidbody2DWhat it actually does
Body Type Dynamic bodies fall and are pushed. Kinematic bodies move only where code puts them, and ignore gravity, forces and mass entirely — if your character stands still in mid-air with Gravity Scale 5, look here first.
Gravity Scale A multiplier on the project-wide gravity (Project Settings, Physics 2D, Y at -9.81). 0 is off, 1 is undiluted. Means nothing on a Kinematic body.
Mass How much the same force moves you. Gravity pulls harder on a heavy body, so mass cancels out in a fall — but a force you apply by hand divides by mass.
Constraints Freeze Rotation Z, set in 2.2 so the character stops tumbling. It matters in the air too: an unfrozen body spins when it clips a corner mid-jump.
TechniqueWhat it requiresWhat it costs
AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse) A Dynamic body and Gravity Scale above 0. Mass divides into it, so retuning mass silently retunes every jump. Clean parabola — the row the walkthrough builds.
linearVelocity = new Vector2(x, jumpSpeed) A Dynamic body and Gravity Scale above 0. Identical height every time, to the pixel — and it erases whatever vertical motion was already there: a platform's lift, a knockback, a bounce.
AddForce(Vector2.up * thrust, ForceMode2D.Force) every frame while held A Dynamic body; some rule that stops it — fuel, ceiling, timer. A rocket, not a jump. Height depends on how long you hold — and this is what you get by accident if you forget the force mode.
MovePosition() in steps A Kinematic body, moved in FixedUpdate(). It ignores gravity, so it never falls, and it only collides with Dynamic bodies — so no floor is solid to it, and no OnCollisionEnter2D(Collision2D collision) fires against those static colliders. The cave door runs on that callback and would go silent.
Architectural specification: how does your script know it is in the cave? Your movement code assumes a top-down world, and the script has to be told which world it woke up in. All four options below are used in real projects; the walkthrough builds (c).
  • (a) Ask the scene its name — SceneManager.GetActiveScene().name against "Platformer". Readable; breaks silently if someone renames the scene.
  • (b) Ask the scene its build index — the cave is index 2. Survives a rename; lies when the build order changes. SceneSwitch.cs already does this.
  • (c) A serialized bool, ticked per scene instance — visible in the Inspector without pressing Play. Costs a tick on every new Player somebody places.
  • (d) A component that only exists on the cave's Player — the component's presence is the flag. Costs one more file and one more lookup.
  • Whatever you pick, reuse a scene-mode flag you already built this year: two flags meaning "which world am I in" is how a project starts rotting.
Architectural specification: gravity and the flick.
  • Where does the cave's gravity get turned on? Name the exact point in the lifecycle — a field default, Start(), or every frame — and say what breaks at the other two.
  • Which key, and which of GetKey or GetKeyDown? Say what holding the key for two seconds does under each.
  • Is your jump force a serialized field or a magic number? Does its name say force or speed — and what does the name make the next reader believe?
  • How do you know the jump worked? Name what you would print, and what it proves.

Socratic: Gravity Is a Number You Own

  1. Real gravity is a constant of the universe. Unity ships it as an Inspector field. Why would an engine make a law of physics editable per object?
  2. Same Player prefab, same script, two worlds: the hub needs Gravity Scale 0 and the cave needs it above 0. Where should that difference live — the script, the scene, or both?
  3. A Kinematic body ignores gravity, forces and mass entirely. Set Body Type to Kinematic in the cave, and what would your jump code have to do instead to look like a jump?
In the CodeFull walkthrough

The question this answers“how does my script know it is in the cave?”

You are editing Assets/Scripts/PlayerController.cs. The cave and the Overworld play the same prefab, so "which world am I in" cannot live on the prefab: it is a fact about the scene, and it has to reach the script before the first frame runs. One field, one gate, one line.

Step 1Declare the flag, and get its starting value right

One field, in the class body beside moveSpeed - the [SerializeField] private pattern from 1.3, in the exact shape of hasAxe from 5.1.

    [SerializeField] private bool isPlatformer = false;
  1. 1Which world this Player woke up in. false is the default, so a Player in any scene nobody has ticked keeps the top-down behaviour it has had since 2.1.The name is not free, and you can check why. Click the cave's Player and look at PlayerController: the scene has carried an override called isPlatformer since before this course existed - a leftover from the version of this script that shipped with the project. Unity stores overrides by field name, so this spelling picks the tick back up where inCave would lose it. Check the box either way.

Step 2Hand the body a gravity scale, once, and only in this world

The body lookup you have had in Start() since 2.2 is unchanged. What is new is one line underneath it, wrapped in the flag.

        body = GetComponent<Rigidbody2D>();

        if (isPlatformer)
        {
            body.gravityScale = 1f;
        }
  1. 1The reference you already cache here - the body about to be given a gravity scale. It sits on the same GameObject as this script, which is why the plain lookup finds it.
  2. 3The gate. Read it as "if this Player is standing in the cave". It is the only thing separating the two worlds.Read once, in a method that runs once. Start() is the once-only setup method from 1.3, and a fact about the scene belongs there.
  3. 5The whole of turning gravity on. body.gravityScale is the Rigidbody2D property behind the Inspector field you have been reading all year, and 1f means "the project gravity, undiluted".Why this is code and not a number in the Inspector. You could set the cave copy to 1 by hand and write nothing - but that number is invisible to a reader of your script and has to be repeated on every new Player anyone places.
Check your work: the finished filebuild it as you read — then open this to check every line

Compare against your own file rather than copying it. The walkthrough wrote an if that only touches the cave; this version decides the number for both worlds in one ternary - the same behaviour and a different argument: "the Overworld keeps 0 because I said so" rather than "because I never mentioned it". Both are right; pick the one you can defend.

// ------------------------------------------------------------------
//  REFERENCE FILE — Assets/Scripts/PlayerController.cs
//  Written to be read, not pasted. Build it with the walkthrough
//  above, one step at a time, then open this and compare.
//  If it is sitting in your project unchanged, that is the thing
//  to go and do now — the walkthrough is still above you.
// ------------------------------------------------------------------
using UnityEngine;

// PlayerController.cs - the gravity half, complete. Everything from 1.4 to 5.3
// (input, movement, animation, coins, health) stays exactly as you wrote it;
// the flag and the one line under it are the whole of what is new.
public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;      // your number from 2.2
    [SerializeField] private bool isPlatformer = false; // which world did I wake up in?

    private Rigidbody2D body;

    void Start()
    {
        body = GetComponent<Rigidbody2D>();             // looked up once, per 2.2

        // Decided once, for both worlds. The Overworld's 0 is written here
        // rather than inherited from the prefab, so the decision is visible
        // to anyone reading this file instead of only to the Inspector.
        body.gravityScale = isPlatformer ? 1f : 0f;
    }

    void Update()
    {
        // ... the input, movement and animation lines from 1.4, 1.6, 2.2 and 5.3 ...
    }
}
In the CodeFull walkthrough

The question this answers“how do I make the character leave the ground?”

You are editing Assets/Scripts/PlayerController.cs. There is no Jump() in Unity, and nobody is going to give you one. The gravity half is already in your file, so what is left is a number to tune, a key check, one push, and a way to see that it worked. The push is where every mistake in this lesson happens.

Step 1Declare how hard the flick is

One field, in the class body next to moveSpeed - and one for the same reason: it is a number a designer tunes, not a number you type into a condition.

    [SerializeField] private float jumpForce = 7f;
  1. 1How hard the flick is. Named jumpForce because that is what it becomes - a force - so no reader mistakes it for the speed the character leaves the ground at.A starting guess, not a value. The number that makes a jump feel right is found in the tuning lab at the bottom of this page. And a force is divided by mass before it becomes motion, so the height 7f buys you depends on the mass the Inspector shows.

Step 2Notice the press, once, at the bottom of Update()

Four lines at the end of the Update() you have been adding to since 1.4 - after your movement and animation lines, beside the key check you wrote for the lever in 5.2.

        if (Input.GetKeyDown(KeyCode.Space))
        {
            body.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        }
  1. 1Input is the class from 1.4, KeyCode.Space names the key, and GetKeyDown is true on the single frame the key goes down.Input.GetKey(KeyCode.Space) here is the wrong one, and it fails in the loudest way there is. Without the Down it is true on every frame the key is held, so half a second of holding Space applies thirty pushes and the character leaves the top of the level. The jump looks broken; the condition is. And the check belongs in Update(): FixedUpdate() is the physics clock, which can run several times in one frame or skip frames, so a one-frame press can be counted twice or missed.
  2. 3The flick. Vector2.up is (0, 1) - straight up - scaled by the field from step 1. Read the call as a sentence: add a force, upward, of this size, as an impulse.The second argument is the whole lesson, and leaving it out is the classic bug. With ForceMode2D.Impulse this is one hit, spent at the next physics step. Without it the default is a push that keeps applying while the calls keep coming - and since this line sits inside a check true on exactly one frame, you get a dribble so small the character twitches and settles. Nothing errors, nothing warns, and "my jump does nothing" is what you write in the group chat. Calling it from Update() is fine, though: the engine queues forces and spends them on its own clock.

Step 3Prove the arc in the Console, then take the proof out

There is no UI for vertical velocity, so the Console is your instrument. One temporary print, at the bottom of Update() where every frame runs.

        Debug.Log("v.y = " + body.linearVelocity.y);
// Delete the print before you move on
  1. 1At the bottom of Update(), outside the key block, with no condition on it. body.linearVelocity is the movement method you chose in 2.2, read instead of written, and .y is the half a jump cares about.That number is the parabola. It jumps to a peak when you press Space, shrinks as gravity takes it back, crosses zero at the top of the arc, and grows negative all the way down. The whole mathematics of a jump, watched rather than written: the apex is not a place in your code, it is the frame this number passes through nothing.
  2. 2An instrument, not a feature. A print left here fills the Console with a thousand lines a minute and buries the next real error under them.
Check your work: the finished filebuild it as you read — then open this to check every line

Compare against your own file rather than copying it. Two differences are worth naming, and neither is a mistake. First the shape: the walkthrough wrote the push inline inside the key check, and this file gives it a method called Jump() - the same line, with a name you can point at when 6.2 and 6.3 start asking what a jump is allowed to do. Second the gravity line: this file decides the number for both worlds with a ternary where the walkthrough only wrote it for one. Your key, your number and the names of your own methods are yours.

// ------------------------------------------------------------------
//  REFERENCE FILE — Assets/Scripts/PlayerController.cs
//  Written to be read, not pasted. Build it with the walkthrough
//  above, one step at a time, then open this and compare.
//  If it is sitting in your project unchanged, that is the thing
//  to go and do now — the walkthrough is still above you.
// ------------------------------------------------------------------
using UnityEngine;

// PlayerController.cs - gravity for the cave, and one flick upward.
// The gravity half came first; the jump assumes it. Up, apex, down: the
// only line that makes the arc is the force, and gravity does the rest.
public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;      // your number from 2.2
    [SerializeField] private float jumpForce = 7f;      // how hard the flick is
    [SerializeField] private bool isPlatformer = false; // which world did I wake up in?

    private Rigidbody2D body;

    void Start()
    {
        // Looked up once, never inside Update() - the rule from 2.2.
        body = GetComponent<Rigidbody2D>();

        // Decided once, for both worlds.
        body.gravityScale = isPlatformer ? 1f : 0f;
    }

    void Update()
    {
        // ... input, movement and animation, from 1.4 to 5.3 ...

        if (Input.GetKeyDown(KeyCode.Space))
        {
            Jump();
        }
    }

    // The whole jump. One impulse, and gravity spends it over the next half
    // second - which is why there is nothing here about coming back down.
    private void Jump()
    {
        body.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    }
}
Build Lab: Tune It ~20 min

Change one number at a time and write one sentence of feel for each, in a file you keep. Three knobs, and the third one is the interesting one.

KnobTry
jumpForce — Inspector → Player Controllerhalf, then double
Gravity Scale — Inspector → Rigidbody2D0.2, then 1, then 4
Mass — Inspector → Rigidbody2D1, then 5 — watch the jump and the fall
Gravity off plus an impulse is not a jump — it is an escape. Leave the cave's Player at Gravity Scale 0 and add an impulse and the character rises and never stops rising. No error, no warning, no ceiling. When a jump "works but feels wrong", read Gravity Scale and Body Type before you touch your code.

Checkpoint — before moving on, you must be able to:

Reference Docs

Need a deeper explanation? These pages cover the same concepts:

Next: 6.2 — Ground Checks: No Infinite Jumps, where you discover that your jump works perfectly in mid-air too.

0/5 answered

Answers save on this device as you type. At the end of class, click Download and upload the file to the Canvas assignment.

Saved on this device only — the Dashboard tracks your progress.