04.making-the-robot-jump

Making the robot jump

In this lesson, you’ll add a jump to the robot.

First, we define how high the jump should be. Thanks to the export keyword, we can change this later in the Inspector.

Just below the previous export lines, and before the velocity line, add this single line:

export var jump_strength := 1400.0

Your file’s head should now look like this:

extends KinematicBody2D

export var speed := 600.0
export var gravity := 4500.0
export var jump_strength := 1400.0

var velocity := Vector2.ZERO

Note: We always group all exported variables at the top, then internal variables (like velocity). There is no particular reason for this. It is just a common convention. Following conventions makes it easier to read other people’s code and for other people to read yours.

Making the character jump

As soon as the player presses the jump button, we want our robot to leap into the air. But we only want the robot to be able to jump if they are on the ground.

So, the order of instructions in the _physics_process() function should be:

  1. Check if the player is pressing “right” or “left,” and apply that to the x axis of velocity.
  2. Add gravity to the y axis of velocity.
  3. New: check if the robot is on the ground.
  4. New: check if the player is pressing “jump.” If they are, and the robot is on the ground, then add strength to the y axis of velocity.
  5. Apply the velocity to the robot.

If we look at the previous code, we can guess where our new code should be:

func _physics_process(delta: float) -> void:
    # Get the horizontal direction as a number between -1.0 and 1.0.
    var horizontal_direction := Input.get_axis("move_left", "move_right")
    # Multiply the direction by speed, and assign to the velocity's x value.
    velocity.x = horizontal_direction * speed
    # Add the gravity to the velocity's y value.
    velocity.y += gravity * delta
    # ---------------------------
    # OUR NEW CODE SHOULD BE HERE
    # ---------------------------
    # Move this kinematic body based on its velocity.
    velocity = move_and_slide(velocity, Vector2.UP)

We create a new variable: is_jumping.

Every frame, we check to see if the player has just pressed the jump button and if the robot is currently on the floor.

If both of these conditions are true, the character can jump that frame.

func _physics_process(delta: float) -> void:
    # ...
    var is_jumping := is_on_floor() and Input.is_action_just_pressed("jump")
    # ...

The is_on_floor() function

The is_on_floor() function found in KinematicBody2D objects lets us check if the object is on the floor.

The engine detects when our robot hits an obstacle. Thanks to the “up direction” Vector2.UP we passed to move_and_slide(), Godot knows this obstacle is the ground and sets the robot as on_floor this frame.

Thanks to that, we can check the robot’s status and set the is_jumping variable accordingly.

Note: the is_on_floor() function only works when the robot collides with the floor. Because we apply gravity every frame, the robot falls and may collide with the floor every frame.

You don’t see it, but under the hood, when the character stands on the ground, this happens every frame:

  1. We apply gravity to the robot, making it fall.
  2. It moves down into the ground.
  3. The physics engine detects that the robot is inside the ground.
  4. It moves the robot back up to just touch the floor and marks it as being on the floor.

Let’s use our is_jumping variable now. Between the is_jumping variable definition, and the move_and_slide() call, we add this:

func _physics_process(delta: float) -> void:
    # ...
    if is_jumping:
        velocity.y = -jump_strength
    # ...

If the robot is jumping, we subtract jump_strength from velocity.y.

The vertical velocity will suddenly be a large negative number, moving the robot up.

But as the _physics_process() function runs, it continuously adds gravity to velocity.y, moving the robot down once more.

Until now, our function looks like so:

func _physics_process(delta: float) -> void:
    # Get the horizontal direction as a number between -1.0 and 1.0.
    var horizontal_direction := Input.get_axis("move_left", "move_right")
    # Multiply the direction by speed, and assign to the velocity's x value.
    velocity.x = horizontal_direction * speed
    # Add the gravity to the velocity's y value.
    velocity.y += gravity * delta
    var is_jumping := is_on_floor() and Input.is_action_just_pressed("jump")
    if is_jumping:
        velocity.y = -jump_strength
    # Move this kinematic body based on its velocity.
    velocity = move_and_slide(velocity, Vector2.UP)

If you run the Level scene now, you’ll be able to jump!

Note: the “jump” action is Space or w by default.

This is good, and we’re almost there.

But the robot might feel strange to control. Even if you barely press the jump button, the robot always does a full-height jump.

Let’s add code to interrupt the jump if the player releases the jump button early.

Cancelling the jump

In many games, such as the Mario games, releasing the jump button early cancels the jump and results in a shorter jump height.

We will allow our players to do that. We introduce another variable: is_jump_cancelled.

A jump is considered cancelled if and only if:

  1. The player just released the “jump” action.
  2. And the robot’s velocity.y is negative: that means the robot is going up. We don’t need to cancel anything if the robot is already moving down (velocity.y would be positive).

We can write the condition right under the is_jumping declaration:

func _physics_process(delta: float) -> void:
    # ...
    var is_jumping := is_on_floor() and Input.is_action_just_pressed("jump")
    # We add the following line
    var is_jump_cancelled := velocity.y < 0.0 and Input.is_action_just_released("jump")
    # ...

Now that we stored the information, we can use it. Let’s add a second branch to our if. If the player has cancelled the jump, we set velocity.y to 0.0 and let gravity take over.

func _physics_process(delta: float) -> void:
    # ...
    if is_jumping:
        velocity.y = -jump_strength
    # We add the following lines
    elif is_jump_cancelled:
        velocity.y = 0.0
    # ...

Now, if you play the scene, you’ll be able to jump to different heights!

We’ll improve the jump even more in the next lesson by adding a double jump.

The code

Here’s the complete code for the robot script so far.

extends KinematicBody2D

export var speed := 600.0
export var gravity := 4500.0
export var jump_strength := 1400.0

var velocity := Vector2.ZERO

func _physics_process(delta: float) -> void:
    # Get the horizontal direction as a number between -1.0 and 1.0.
    var horizontal_direction := Input.get_axis("move_left", "move_right")
    # Multiply the direction by speed, and assign to the velocity's x value.
    velocity.x = horizontal_direction * speed
    # Add the gravity to the velocity's y value.
    velocity.y += gravity * delta

    # Verify if "jump" was pressed while character is on floor.
    var is_jumping := is_on_floor() and Input.is_action_just_pressed("jump")
    # Verify if "jump" was released while character is rising.
    var is_jump_cancelled := velocity.y < 0.0 and Input.is_action_just_released("jump")

    # If jumping, subtract jump_strength from velocity.y.
    # But if player cancelled jump instead, set velocity.y to 0.0.
    # Otherwise, leave velocity.y as it is.
    if is_jumping:
        velocity.y = -jump_strength
    elif is_jump_cancelled:
        velocity.y = 0.0

    # Move this kinematic body based on its velocity.
    velocity = move_and_slide(velocity, Vector2.UP)