Welcome back to the tutorial series on how to write your first platformer in PyGame. In this fourth part we are going to look at making the player character jump!

If you’re just starting out please start right from the beginning at Part 1.

At the moment we can draw a player

So far we can draw the player character and move it to the left and right when the arrow keys are pressed.

The game code so far:

# import the PyGame library
import pygame
from pygame.locals import *

# initialise PyGame
pygame.init()
pygame.display.set_caption("Platformer")

# settings
HEIGHT = 450
WIDTH = 400
FPS = 60
LEFT_RIGHT_MOVE = 5

# the clock and display surface
FramePerSec = pygame.time.Clock()
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))

# the Player Sprite
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 15,15 is the width and height
        self.surf = pygame.Surface((15, 15))
        self.surf.fill((128,255,40))
        self.rect = self.surf.get_rect()
        self.rect.midbottom = (200, 225)

# visible elements
P1 = Player()
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)

# the game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
    displaysurface.fill((0, 0, 0))

    # move the player left/right
    keys = pygame.key.get_pressed()
    if keys[K_LEFT]:
        P1.rect.left -= LEFT_RIGHT_MOVE
    if keys[K_RIGHT]:
        P1.rect.right += LEFT_RIGHT_MOVE

    # stop at the edge
    if P1.rect.left < 0:
        P1.rect.left = 0
    if P1.rect.right > WIDTH:
        P1.rect.right = WIDTH

    # draw all visible elements
    for entity in all_sprites:
        displaysurface.blit(entity.surf, entity.rect)

    pygame.display.update()
    FramePerSec.tick(FPS)

Making the player jump – part 1: gravity

The next step in this tutorial is to make the player jump up when the user presses the UP arrow on the keyboard.

All the code changes will happen in the game loop again.

Part of jumping up is falling back down again, so let’s start with that first of all. We insert some code in the game loop that moves the player down each round. This simulates gravity – without any platforms or anything else in the way the player will drop down-wards.

When the player gets to the bottom of the screen it would keep falling and disappear. To prevent this we add a bit more code so that it stops when it reaches the bottom.

    # move the player left/right
    keys = pygame.key.get_pressed()
    if keys[K_LEFT]:
        P1.rect.left -= LEFT_RIGHT_MOVE
    if keys[K_RIGHT]:
        P1.rect.right += LEFT_RIGHT_MOVE

    # stop at the edge
    if P1.rect.left < 0:
        P1.rect.left = 0
    if P1.rect.right > WIDTH:
        P1.rect.right = WIDTH

    # gravity
    P1.rect.bottom += 4

    if P1.rect.bottom > HEIGHT:
        P1.rect.bottom = HEIGHT

    # draw all visible elements
    for entity in all_sprites:
        displaysurface.blit(entity.surf, entity.rect)

This line P1.rect.bottom += 4 moves the player down the screen. Remember that the coordinate system starts at the top-left corner of the game, and higher values for the y-axis move down the screen. You can change the number 4 to something else to experiment how it feels with different levels of gravity.

Type out this code and try it out. The player should fall to the bottom of the game screen and then stay there. You can still move left and right with the arrow keys.

Making the player jump – part 2: jumping

Falling is great but how do we make the player jump? Remember the left/right key press detection? We can use the same kind of code to check for the UP arrow, and then move the player up the screen when the button is pressed.

Type in the new code into the game loop:

    # stop at the edge
    if P1.rect.left < 0:
        P1.rect.left = 0
    if P1.rect.right > WIDTH:
        P1.rect.right = WIDTH

    # jump
    if keys[K_UP]:
        P1.rect.bottom -= 20

    # gravity
    P1.rect.bottom += 4

    if P1.rect.bottom > HEIGHT:
        P1.rect.bottom = HEIGHT

    # draw all visible elements
    for entity in all_sprites:
        displaysurface.blit(entity.surf, entity.rect)

This line: P1.rect.bottom -= 20 moves the player up the screen – remember that the y-axis is inverted, meaning y gets bigger the further down the screen you go. And we’re using the same short-hand way of subtracting numbers with the -= operator.

Experiment with values other than 20 for the jump amount, and different values than 4 for the gravity amount until you end up with something that works well for you.

Making the player jump – part 3: better gravity

Depending on how fussy you are you might be happy with the jumping code. Me personally I find it quite clunky and it doesn’t feel smooth enough for a game.

The main problem is that in real life gravity doesn’t just make something fall by a fixed amount each time. When you drop a ball from a tower, for example, it will fall faster and faster. That is because gravity is a force acting on the ball, and the force results in acceleration. The acceleration makes the ball fall faster and faster. More accurately – the force from gravity accelerates the ball, which makes its speed increase, which moves the ball faster and faster.

The same thing happens when driving a car. It starts with a speed of 0. The driver pushes the pedal down, which accelerates the car. The acceleration increases the speed more and more, making the car move faster and faster along.

We want to have the same kind of physical happening in our game. The key factors are speed and acceleration through gravity.

Here is the new code to replace the jumping and gravity code in the game loop. I’ve given you the entire code block again because we also have another line outside of the game loop. There are comments in the code to explain what is going on.

# import the PyGame library
import pygame
from pygame.locals import *

# initialise PyGame
pygame.init()
pygame.display.set_caption("Platformer")

# settings
HEIGHT = 450
WIDTH = 400
FPS = 60
LEFT_RIGHT_MOVE = 5

# the clock and display surface
FramePerSec = pygame.time.Clock()
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))

# the Player Sprite
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        # 15,15 is the width and height
        self.surf = pygame.Surface((15, 15))
        self.surf.fill((128,255,40))
        self.rect = self.surf.get_rect()
        self.rect.midbottom = (200, 225)

# visible elements
P1 = Player()
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)

# a variable to store the current vertical speed
speed_y = 0

# the game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
    displaysurface.fill((0, 0, 0))

    # move the player left/right
    keys = pygame.key.get_pressed()
    if keys[K_LEFT]:
        P1.rect.left -= LEFT_RIGHT_MOVE
    if keys[K_RIGHT]:
        P1.rect.right += LEFT_RIGHT_MOVE

    # stop at the edge
    if P1.rect.left < 0:
        P1.rect.left = 0
    if P1.rect.right > WIDTH:
        P1.rect.right = WIDTH

    # jumping changes speed in the opposite direction
    # experiment with different values
    if keys[K_UP]:
        speed_y = -8

    # move the 
    P1.rect.bottom += speed_y

    # gravity accelerates - the speed gets higher each time
    # experiment with different values
    speed_y += 0.5

    # stop at the bottom of the screen
    if P1.rect.bottom > HEIGHT:
        P1.rect.bottom = HEIGHT
        speed_y = 0

    # draw all visible elements
    for entity in all_sprites:
        displaysurface.blit(entity.surf, entity.rect)

    pygame.display.update()
    FramePerSec.tick(FPS)

This might look a bit more complicated but don’t let it overwhelm you. Just go through each line and see if you can work it what it does. Then experiment with different values until you have a jumping and falling effect that works for you!

Next Steps

Continue with Part 5 of the tutorial. We will start adding some platforms to make it feel like a fully rounded game.