Let’s get started with Part 6 of our PyGame platformer tutorial. If you’ve landed directly here it’s best to start from Part 1.
To recap what we have so far:
- our game can draw the player and the platforms
- the user can move their player left and right with the arrow keys on the keyboard
- the user can jump their player with the up arrow key
- gravity makes the player fall down to the bottom of the screen
In this Part 6 of the series we will make sure that the player lands on platforms rather than jump straight through them.
Before we get started here is the 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)
# the Platform Sprite
class Platform(pygame.sprite.Sprite):
def __init__(self, width, position, colour = (255,0,0)):
super().__init__()
self.surf = pygame.Surface((width, 20))
self.surf.fill(colour)
self.rect = self.surf.get_rect(center = position)
platform_1 = Platform(100, (200, 330))
platform_2 = Platform(50, (150, 400))
platform_3 = Platform(120, (390, 260))
platform_4 = Platform(190, (95, 190))
platform_5 = Platform(160, (370, 120))
# visible elements
P1 = Player()
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)
all_sprites.add(platform_1)
all_sprites.add(platform_2)
all_sprites.add(platform_3)
all_sprites.add(platform_4)
all_sprites.add(platform_5)
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
# jump
if keys[K_UP]:
speed_y = -8
# gravity
P1.rect.bottom += speed_y
speed_y += 0.5
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)
Landing on a platform – checking the player’s position
Before we get stuck into the code it’s best to work out on paper as to when a player would have landed on a platform. It all depends on the player’s position. Let’s take a look at the conditions that determine whether player has landed on a platform or not.
Imagine these different cases:

In each case the player is falling downwards due to gravity. Let’s see what difference the position makes on whether or not they will land on the platform.
- In case 1 the player is too far to the left of the platform to land.
- In case 2 the player will land on the platform.
- At first, in 2a, it hasn’t landed yet – it’s too far up
- In 2b it also hasn’t landed yet – it’s still too far up
- In 2c it is overlapping the edge of the platform. This is the point at which it would have landed and it’s this situation that we need to detect in the code.
- You might think that the player should have landed exactly on the edge of the platform. But this is almost impossible since the player falls down a block of pixels each time due to gravity. So we should detect whether it’s overlapping to see that it has landed.
- In case 3 the player is too far to the right of the platform to land.
Let’s convert these different cases into sentences:
- We want the player to NOT be too far left.
- We want the player to have overlapped with the top of the platform.
- We want the player to NOT be too far to the right.
Here are some more pictures to show each condition. Can you write down conditions using the top/left/right positions of the player and the platform to check whether it has landed?
Let’s start with the first condition – the player should NOT be too far to the left:

From the image we can see that when the player is NOT too far to the left then its right edge should be further than the platform’s left edge. Or, as a code inequality:
player.right > platform.left
Then let’s look at the overlap with the top of the platform:

When it overlaps we have the following conditions:
player.bottom > platform.top AND player.top < platform.top
Remember that the y-axis is inverted, meaning y values get bigger the further you go down the screen.
Finally let’s look at the right-hand edge of the platform:

From the image we get this third condition:
player.left < platform.right
Bringing this all together we get this one long condition to check whether the player has landed on a platform:
player.right > platform.leftand player.bottom > platform.top
and player.top < platform.top
and player.left < platform.right
Making the player land
Before we put all this into our game code let’s figure out what it should look like when the player has landed.

- We want the player to sit on the top of the platform.
- We don’t want the player to fall anymore.
In code we can write it like this:
player.bottom = platform.top
speed_y = 0
Adding the landing logic to the game code
Ok, let’s bring it all together and add the landing logic to the game code.
The logic will go like this:
- every time the game loop runs we will:
- check each platform to see whether the player has landed on it
- if the player has landed then we will put the player on top of the platform and stop falling
- if the player hasn’t landed we will keep falling until we hit the bottom of the screen
Here is the updated code with the new logic. Because we have to check each platform we have added a new function called has_landed to the Platform class.
Please type in the code and run the game to see how it works.
# 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)
# the Platform Sprite
class Platform(pygame.sprite.Sprite):
def __init__(self, width, position, colour = (255,0,0)):
super().__init__()
self.surf = pygame.Surface((width, 20))
self.surf.fill(colour)
self.rect = self.surf.get_rect(center = position)
platform_1 = Platform(100, (200, 330))
platform_2 = Platform(50, (150, 400))
platform_3 = Platform(120, (390, 260))
platform_4 = Platform(190, (95, 190))
platform_5 = Platform(160, (370, 120))
# this new platform is the "ground" of the game
platform_6 = Platform(WIDTH, (WIDTH/2, HEIGHT), (0, 0, 255))
# has it landed?
# this function checks whether the player has landed on
# a platform
def has_landed(player:Player, platform:Platform)->bool:
return (
player.rect.right > platform.rect.left and
player.rect.bottom >= platform.rect.top and
player.rect.top < platform.rect.top and
player.rect.left < platform.rect.right
)
# visible elements
P1 = Player()
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)
all_sprites.add(platform_1)
all_sprites.add(platform_2)
all_sprites.add(platform_3)
all_sprites.add(platform_4)
all_sprites.add(platform_5)
all_sprites.add(platform_6)
# we put all platforms into a group so we can check
# each one to see whether the player has landed
all_platforms = pygame.sprite.Group()
all_platforms.add(platform_1)
all_platforms.add(platform_2)
all_platforms.add(platform_3)
all_platforms.add(platform_4)
all_platforms.add(platform_5)
all_platforms.add(platform_6)
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
# has it landed on a platform?
player_has_landed = False
for platform in all_platforms:
if has_landed(P1, platform):
P1.rect.bottom = platform.rect.top
speed_y = 0
player_has_landed = True
# the 'break' comes out of the for-loop
# the player can only land on one platform
# so we don't have to check the others
break
if player_has_landed:
# if player has landed we can also jump
if keys[K_UP]:
speed_y = -8
# move the player off the platform
# otherwise we will think it has landed again
# and won't actually do the jump
P1.rect.bottom -= 1
else:
# if player hasn't landed they are effected by gravity
P1.rect.bottom += speed_y
speed_y += 0.5
# we now have a platform at the bottom so we don't need to check the bottom of the screen anymore (delete this code)
# 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)
And here is the game with the player landed on a platform:

Bumping into a platform from below
When you play the game now it acts a bit strangely – the player suddenly jumps up quite through the platforms. This is because we don’t check whether the player bumps into the platforms from below.
Here is a another graphic to show what bumping looks like:

Just like before we can look at the position of the player and the position of the platform, and come up with this code condition:
player.right > platform.left
and player.left < platform.right
and player.top < platform.bottom
and player.bottom > platform.bottom
(remember the y-axis is inverted, y gets bigger as you move down the screen)
And when the player has bumped into the platform we want to make sure it sits flush with the bottom edge of the platform and stops moving:
player.top = platform.bottom
speed_y = 0
Let’s bring it all together into the game code:
# 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)
# the Platform Sprite
class Platform(pygame.sprite.Sprite):
def __init__(self, width, position, colour = (255,0,0)):
super().__init__()
self.surf = pygame.Surface((width, 20))
self.surf.fill(colour)
self.rect = self.surf.get_rect(center = position)
platform_1 = Platform(100, (200, 330))
platform_2 = Platform(50, (150, 400))
platform_3 = Platform(120, (390, 260))
platform_4 = Platform(190, (95, 190))
platform_5 = Platform(160, (370, 120))
platform_6 = Platform(WIDTH, (WIDTH/2, HEIGHT), (0, 0, 255))
# has it landed?
def has_landed(player:Player, platform:Platform)->bool:
return (
player.rect.right > platform.rect.left and
player.rect.bottom >= platform.rect.top and
player.rect.top < platform.rect.top and
player.rect.left < platform.rect.right
)
# has it bumped?
def has_bumped(player:Player, platform:Platform)->bool:
return (
player.rect.right > platform.rect.left and
player.rect.top < platform.rect.bottom and
player.rect.bottom > platform.rect.bottom and
player.rect.left < platform.rect.right
)
# visible elements
P1 = Player()
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)
all_sprites.add(platform_1)
all_sprites.add(platform_2)
all_sprites.add(platform_3)
all_sprites.add(platform_4)
all_sprites.add(platform_5)
all_sprites.add(platform_6)
all_platforms = pygame.sprite.Group()
all_platforms.add(platform_1)
all_platforms.add(platform_2)
all_platforms.add(platform_3)
all_platforms.add(platform_4)
all_platforms.add(platform_5)
all_platforms.add(platform_6)
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
# has it landed on a platform?
player_has_landed = False
for platform in all_platforms:
if has_bumped(P1, platform):
P1.rect.top = platform.rect.bottom
speed_y = 0
# come out of the loop - we can only bump
# into one platform
break
if has_landed(P1, platform):
P1.rect.bottom = platform.rect.top
speed_y = 0
player_has_landed = True
break
if player_has_landed:
# if player has landed we can also jump
if keys[K_UP]:
speed_y = -8
P1.rect.bottom -= 1
else:
# if player hasn't landed they are effected by gravity
P1.rect.bottom += speed_y
speed_y += 0.5
# draw all visible elements
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
pygame.display.update()
FramePerSec.tick(FPS)
Next Steps
Now it’s your turn! Make sure you’ve typed in all the code and the game runs. If there are any problems compare your code with the code above and see that it matches.
Congratulations, you almost have a finished platformer! We’ll still continue the tutorial in the future if you want to follow along – or perhaps you have more ideas on how to change the game yourself?
Read on to Part 7 to add a Target area and a simple ‘end of game’ animation.
Leave a Reply