Welcome back – this is part 7 of the series on writing your first ever platformer game using the popular PyGame python library.
In Part 1 to Part 6 we created a working game with:
- a player character that moves left and right and jumps
- platforms that the player can jump onto and bump into from below
- gravity that makes the player fall when they are not on a platform
In this Part 7 we will create a target area that ends the game when the player lands on it.
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))
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
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)
And here is a screenshot of our progress:

We’re looking to add a small rectangle at the top that will be the target area. When you reach it the screen will flash in different colours.
Drawing the target area
When we want to draw something on the screen we need a Sprite! So let’s create a new Sprite just for the target area:
class Target(pygame.sprite.Sprite):
def __init__(self, position):
super().__init__()
self.surf = pygame.Surface((20, 20))
self.surf.fill((30, 184, 30))
self.rect = self.surf.get_rect(center = position)
This creates a Target of 20×20 pixels size, with a colour of (30, 184, 30) – which is a light green.
We then create an instance of the Target sprite and add it to the list of visible elements:
T = Target((390, 100))
all_sprites.add(T)
Let’s add these pieces to the main code block:
# 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 Target Sprite
class Target(pygame.sprite.Sprite):
def __init__(self, position):
super().__init__()
self.surf = pygame.Surface((20, 20))
self.surf.fill((30, 184, 30))
self.rect = self.surf.get_rect(center = position)
# 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, (250, 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
all_sprites = pygame.sprite.Group()
T = Target((390, 100))
all_sprites.add(T)
P1 = Player()
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
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)
I also adjusted the position of platform 3 a little – it was impossible to jump onto it.
By adding the Target before the Player we make sure that the Player will be drawn on top of the target, so both will remain visible. You can change the order on the screen by changing the order that your sprites are added to the all_sprites list.
This code places the target at the top of the game and your screen should now look something like this:

Landing on the Target
When the player lands on the target we want to make the screen flash. For this we will need to use something called ‘hit detection’. Hit detection is the process of checking whether your player has ‘hit’ something else on the screen.
Luckily for us, Pygame has some hit detection methods built in. Here is a list of them:
| pygame.Rect.contains | test if one rectangle is inside another |
| pygame.Rect.collidepoint | test if a point is inside a rectangle |
| pygame.Rect.colliderect | test if two rectangles overlap |
| pygame.Rect.collidelist | test if one rectangle in a list intersects |
| pygame.Rect.collidelistall | test if all rectangles in a list intersect |
| pygame.Rect.collideobjects | test if any object in a list intersects |
| pygame.Rect.collideobjectsall | test if all objects in a list intersect |
| pygame.Rect.collidedict | test if one rectangle in a dictionary intersects |
| pygame.Rect.collidedictall | test if all rectangles in a dictionary intersect |
Different methods are useful for different types of hit detection. For our game we will use the first one pygame.Rect.contains – it can check whether the Player character has landed fully in the target area.
Because the player moves around we need to keep checking for the target area. This means the code will go into our game loop:
# 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 Target Sprite
class Target(pygame.sprite.Sprite):
def __init__(self, position):
super().__init__()
self.surf = pygame.Surface((20, 20))
self.surf.fill((30, 184, 30))
self.rect = self.surf.get_rect(center = position)
# 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, (250, 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
all_sprites = pygame.sprite.Group()
T = Target((390, 100))
all_sprites.add(T)
P1 = Player()
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
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
# reached the target?
if T.rect.contains(P1.rect):
print("FINISHED")
# draw all visible elements
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
pygame.display.update()
FramePerSec.tick(FPS)
For now we just print the text “FINISHED”. The print() function is useful to test that your code works correctly. It writes the text to the log window. In the Visual Studio Code editor it appears in a separate tab in the terminal:

Playing an animation when reaching the target
To tell the player they’ve reached the target we’re going to play a simple animation. To do this we will make the background flash in random colours.
Here is the new code:
# import the PyGame library
import pygame
import random
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 Target Sprite
class Target(pygame.sprite.Sprite):
def __init__(self, position):
super().__init__()
self.surf = pygame.Surface((20, 20))
self.surf.fill((30, 184, 30))
self.rect = self.surf.get_rect(center = position)
# 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, (250, 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
all_sprites = pygame.sprite.Group()
T = Target((390, 100))
all_sprites.add(T)
P1 = Player()
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
on_target = False
# the game loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if on_target:
background_colour = (
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255)
)
else:
background_colour = (0, 0, 0)
displaysurface.fill(background_colour)
# 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
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
# reached the target?
on_target = T.rect.contains(P1.rect)
# draw all visible elements
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
pygame.display.update()
FramePerSec.tick(FPS)
Let me explain how this works.
We’ll start with the hit detection. There is no more if-statement, so how does this work?
T.rect.contains(P1.rect) – this code checks whether the Player has landed on the Target. It returns a value of True or False. We store that value in a variable called on_target so that we know whether or not we should animate the background colour.
Here is the background colour animation code:
if on_target:
background_colour = (
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255)
)
else:
background_colour = (0, 0, 0)
displaysurface.fill(background_colour)
Reading this from top to bottom we see that:
- if the Player is on target, then we don’t use a fixed background colour. Instead we use 3 random values for each of the R, G and B components of the background colour
- if the Player is NOT on target it will use the
else:version, which sets the background colour to(0, 0, 0), ie. black - the chosen colour is used to fill the game’s background
The code runs in the game loop, so the background is redrawn in a new colour each time, resulting in the animation effect.
Simple really!
Perhaps you can change the code around to come up with a better animation?
Next Steps
This is the final part of the tutorial series, for now. Feel free to play around with the game code, change the numbers for gravity, create your own platform layout, create your own version of the ‘end of game’ animation.
The only limit is your imagination!
Share your version of the game in the comments below.
Thanks for reading and please post in the comments if you have any feedback or questions! Happy coding!
Leave a Reply