Nils Millahn - DN Digital Ltd

Tech and Productivity

How to write your first PyGame platformer – Part 7

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.containstest if one rectangle is inside another
pygame.Rect.collidepointtest if a point is inside a rectangle
pygame.Rect.colliderecttest if two rectangles overlap
pygame.Rect.collidelisttest if one rectangle in a list intersects
pygame.Rect.collidelistalltest if all rectangles in a list intersect
pygame.Rect.collideobjectstest if any object in a list intersects
pygame.Rect.collideobjectsalltest if all objects in a list intersect
pygame.Rect.collidedicttest if one rectangle in a dictionary intersects
pygame.Rect.collidedictalltest 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!

How to write your first PyGame platformer – Part 6

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.

  1. In case 1 the player is too far to the left of the platform to land.
  2. 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.
  3. In case 3 the player is too far to the right of the platform to land.

Let’s convert these different cases into sentences:

  1. We want the player to NOT be too far left.
  2. We want the player to have overlapped with the top of the platform.
  3. 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.

How to write your first PyGame platformer – Part 5

Welcome back to this tutorial series on how to write your first platformer in PyGame. This is Part 5 – if you’ve haven’t seen the previous parts it’s best to start at the beginning with Part 1.

A quick recap of what happened in Part 4:

  • we added gravity, making the player character fall down the screen
  • we added jumping when the UP arrow is pressed

In this part of the tutorial we will start adding platforms to the game.

Before we get started, here is the complete 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)

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)

Adding platform graphics – more Sprites!

Let’s add some platform graphics to the game. Remember how we used a Sprite class to create the player character? Platforms also need to be displayed on the screen, so we are going to use another Sprite class to create them.

Here is the new code for 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)

Notice this line: self.surf = pygame.Surface((width, 20)) – it sets the height of the platform to 20 pixels.

So how to you create a new Platform with this code? Since it’s a Python class you can create new instance of it, and each instance will become a new platform in the game.

The Platform class takes 3 parameters:

  1. The width of the platform, in pixels
  2. The position of the platform – this is a (x, y) coordinate pair
  3. Optionally a third parameter with the colour of the platform. This is a (red, green, blue) tuple (more on tuples below!). If you don’t pick a colour then red will be used by default.

With this in mind you create new platforms by using code like this:

platform_1 = Platform(100, (200, 330))
platform_2 = Platform(50, (150, 400))

These lines create 2 platforms – the first is 100 pixels wide and positioned at x=200, y=300. The second is 50 pixels wide and positioned at x=150, y=400.

To draw the platforms on the screen you also need to make sure they are included in the game loop’s drawing code. To do this you would add the platform sprites to the all_sprites collection:

all_sprites.add(platform_1)
all_sprites.add(platform_2)

Change your game code to include these extra lines, highlighted in bold green:

# 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))

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

Once you’ve done that your game screen should look like this:

A quick word about tuples

You might have noticed some odd looking numbers in brackets a few times in the code, like the position of the platforms as (200, 330) for example, or the colour values that look like (255, 0, 0). These lists of numbers are grouped together using brackets. These things are called tuples. They are a key feature of the Python programming language and it’s useful to know more about them.

In Python when you have some numbers or other values that belong together, you can group them together as a list. You can use them in your code as a single value which is more convenient and easier to write.

One example is setting the position of the Player. The code looks like this:

self.rect.midbottom = (200, 225)

midbottom is a point on the screen and it needs an x and a y coordinate. Instead of setting each value separately the code uses a tuple, which combines the x and y coordinates into a single variable.

Similarly to set the colour of the player’s character we use this code:

self.surf.fill((128,255,40))

The colour needs 3 values – the Red, Green, Blue components – and instead of setting each one separately we can use a tuple (128, 255, 40) instead.

The tuple is always wrapped in its own set of brackets, so here we end up with double pairs of brackets. The first set is for the fill() command, the second set is for the colour tuple.

Next Steps

Now that you have added a few platforms to the game, experiment with the platform code to create a level that looks good to you. Make sure there are enough platforms to fill the screen from top to bottom, and that they seem within reach of your jumping player character.

For example after some tweaking I’ve come up with this level design:

I’m looking forward to seeing what you can come up with!

In the next tutorial (not written yet!) we will look at making the player character land on the platforms. Keep checking back!

How to write your first PyGame platformer – Part 4

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.

How to write your first PyGame platformer – Part 3

Welcome to Part 3 of this PyGame platformer tutorial, which I’m writing for my eldest son. So far we’ve covered the basic game loop in Part 1, and drawn the player character in Part 2. We will now look at moving the player character with the left/right arrows keys on the keyboard.

First though here is the code so far!

# import the PyGame library
import pygame

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

# settings
HEIGHT = 450
WIDTH = 400
FPS = 60

# 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))

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

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

Handling keyboard presses – left/right arrows

To move the character we need to know when the player has pressed the left/right arrows on the keyboard.

It’s really easy in PyGame – you can call pygame.key.get_pressed() which returns a list of all the keys that the player is holding down right now.

Remember that the game loop runs 60 times per second, so what we can do in the game loop is this:

  1. check pygame.key.get_pressed()
  2. if the user is holding down the left arrow, move the player to the left by a few pixels
  3. if the user is holding down the right arrow, move the player to the right by a few pixels

Here is the new game code, with movement code inserted:

# 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

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

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

Note this line: LEFT_RIGHT_MOVE = 5 – this sets by how many pixels the player moves left or right each time the game loop runs. Make this bigger to move faster, smaller to move more slowly. Have a go and choose a number that feels right to you.

Also note these two operators: -= and += – they might look a little strange. They are a shorter way of subtracting or adding a number. So:

P1.rect.left -= LEFT_RIGHT_MOVE

subtracts LEFT_RIGHT_MOVE pixels (in our case 5 pixels) from the current left position of the player’s rectangle. This moves the player’s character to the left.

And

P1.rect.left += LEFT_RIGHT_MOVE

adds LEFT_RIGHT_MOVE (5 pixels, remember) to the current left position of the rectangle. This moves the player’s character to the right.

Type out the new code and run the game. See how the player moves left and right when you hit the arrow keys?

Using other keys

You might not want to use the arrows keys – after all many games use ASDW to move the player around. You can change the code really easily to use different keys instead.

To use A to go left, change:

if keys[K_LEFT]: to if keys[K_a]:

To use D to go right, change:

if keys[K_RIGHT]: to if keys[K_d]:

PyGame has these constants built in for each key on the keyboard – just add the letter to K_ and you can check whether it’s pressed. Number keys for example are K_0 all the way to K_9 for example.

Have a go and change the keyboard triggers for moving the player around.

Dealing with the edge of the screen

When you play the game right now you’ll see that if you press the left arrow long enough the player disappears off the edge of the screen. You can bring it back by pressing the right arrow – it’s not really gone, it’s just moved too far left to be displayed by the game.

This isn’t great – let’s see what we can do about it.

One idea is that it could stop at the edge of the screen. Remember that the coordinate system starts at 0 and goes all the way to the width of the game, stored in the WIDTH variable. PyGame also gives us handy values on the rect property to check the left/right position of the rectangle.

Using this information we end up with a new movement code to replace the code we already have in the game loop:

# 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

How does this work?

When the player’s rectangle has gone off the left-hand side of the screen, its left coordinate will be less than zero. In this case we reset the left position to 0, so it’s snug against the left-hand side.

On the right-hand side we check whether the right edge of the rectangle has gone off the screen. This happens when it’s gone further than the WIDTH of the game. When it happens we reset the position to be exactly the WIDTH, so that the player is snug against the right side of the screen.

Alternative: Looping around the screen

Instead of stopping at the edges we can also loop around the screen. See if you can write this code yourself using the left/right if-statements from before.

Once you’ve tried, compare it with this code example. (If the code isn’t exactly the same it doesn’t matter, you might have found a different way of doing it!)

# 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

# loop around
if P1.rect.left < 0:
    P1.rect.right = WIDTH
if P1.rect.right > WIDTH:
    P1.rect.left = 0

How does this work? Well, when the left edge of the player has gone off the left-hand side of the screen (left < 0) we set the right edge to be the WIDTH of the screen – this pops the player round exactly to the right-hand side.

Also when the right edge has gone off the right-hand side (right > WIDTH) we set the left edge to be at 0 – this pops the player round exactly to the left-hand side.

See which version you like most and use that one in your game!

I like the ‘stop at the edges’ approach, and with that in place here is the final code for this part of the tutorial:

# 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)

Next Steps

In Part 4 we’re going to add gravity and make the player jump!

How to write your first PyGame platformer – Part 2

Welcome back to Part 2 of this tutorial series on writing your first platformer game. This is a series I’m writing for my eldest son (if you are reading this – keep doing what you’re doing!) and it is based on the PyGame python code library.

In case you’ve missed it you can read Part 1 here!

A quick overview of what was covered in Part 1:

  • initialise PyGame
  • create the basic game loop

To save time here is the final code from Part 1:

# import the PyGame library
import pygame

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

# settings
HEIGHT = 450
WIDTH = 400
FPS = 60

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

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

Here is what will be covered in Part 2:

  • drawing the player character on the screen

Drawing in PyGame – all about Sprites

Anything you see on the screen in PyGame is done using Sprites. According to the documentation a sprite is a “simple base class for visible game objects” (you can read more about it in the official Sprite documentation page)

We are going to use a Sprite to create the player’s own character. To keep it simple we’re going to use a small square.

The end result is going to look like this:

Let’s add the code to draw the player’s character.

From now on we will keep adding code to what we’ve already written – the full code is at the end of the post as well in case you find it easier to follow along that way.

The new code is highlighted below:

# import the PyGame library
import pygame

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

# settings
HEIGHT = 450
WIDTH = 400
FPS = 60

# 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))

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

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

The code in the game loop draws the graphic to the screen.

What the player character looks like, its size, position and colour, are defined in the Player class. Experiment for a bit to understand better how the code works:

  1. self.surf = pygame.Surface((15, 15))

    The (15, 15) here sets the size of the player – 15×15 pixels. Experiment with different values until you are happy with it.
  2. self.surf.fill((128,255,40))

    The (128, 255, 40) is the player’s colour. This is a so-called RGB value – red, green and blue. Each of the three numbers goes from 1 to 255 and determines how much of red, green, blue to mix together to make the final colour. You can use this online RGB mixer to come up with new values, or just type in new numbers (from 1 to 255, remember!) to experiment.
  3. self.rect.midbottom = (200, 225)

    The values (200, 225) sets the position of the player. This is in the x-y-coordinate system that you might already be used to. The first value (200) is the x coordinate, the second (225) is the y coordinate. Everything is measured from the top left of the screen.

    In our case because the width of the game is set to 400 and the height is set to 450 this places the character in the middle of the screen.

    Another thing to mention is that the rect here is the rectangle shape of the player. And we’re settings its midbottom coordinate. This places the middle point of the bottom edge of the player’s rectangle shape at the (200, 225) coordinate. In PyGame the rect property has other points that you can set the coordinates for – topleft, bottomleft, topright, bottomright midtop, midleft, midbottom, midright center, centerx, centery – you can experiment with these too to see how it affects the player’s position.

Make sure you experiment with this, change the player’s size, position and colour, until you’re happy that you understand how the code works.

Once you’ve done that it’s time to move on to Part 3, where you move the player character with the left/right arrows on the keyboard.

How to write your first PyGame platformer – Part 1

Recently my son won a bet and as a prize he asked me to teach him how to write a platformer game in PyGame. I love seeing where his imagination takes him, and so, with some Python coding experience already under my belt, I decided to have a go.

What is included

This is Part 1 of a series of platformer tutorials. It aims to cover the following:

  • Basic game setup, with PyGame initialisation and the game loop

I’ll continue with the series until we’ve got a playable game. The direction will be shaped by my son’s input, since ultimately he is the target audience.

Here is a screenshot of the finished game. Don’t expect too much from the graphics at this stage, we might get round to later on.

My PyGame programming setup

I’m not a Python developer by trade – although I’ve worked on some Python and Django projects in the past. So I’ve decided to use the tools that I’m familiar with. In this case it’s Visual Studio Code as the code editor. The tools inside the code editor also let me run and debug the PyGame code which is a great help.

My son uses a cloud-based code editor called Replit at school. My goal is that he will be able to copy-paste the code into Replit and run it online without any problems. The simpler the better really. In my experience getting results quickly is the best motivation.

If you’re still looking for information on how to get started with PyGame try heading over to their Getting Started Page. For me it helped but personally I don’t think it’s written with beginners in mind. The language used assumes existing technical knowledge and some knowledge on Python in general. The way that PyGame is taught in schools these days that’s definitely not a given!

Another site I’ve found useful is this PyGame Primer. In the section Background and Setup it shows you how to install PyGame and run it via the command line. Sort of useful if you’re already familiar with command lines and the pip command. I was, so it helped – but if you’re very new to programming this too could be too complex.

Let me know in the comments which starting guide you’ve found the most helpful!

Ok, enough setup – let’s get started with the tutorial.

Initialising PyGame

There are certain things that you have to do for every PyGame game – whether it’s a platformer or any other kind of game. These are:

  1. Import the PyGame code library
  2. Initialise PyGame in your code
  3. Set up a basic game loop

This can all be done in a single file with only a few lines of code. Here is an example:

import pygame

pygame.init()
pygame.display.set_caption("Platformer")

HEIGHT = 450
WIDTH = 400
FPS = 60
FramePerSec = pygame.time.Clock()
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
    displaysurface.fill((0, 0, 0))
    pygame.display.update()
    FramePerSec.tick(FPS)

This is the most basic PyGame starter code that will open up a PyGame window. It’s not super exciting – it’s just a blank screen that looks like this:

Try to get this running first though – if it works then it means that your overall setup is correct and you can safely continue coding.

If it doesn’t work then go back to one of the Getting Started PyGame pages to check that you have installed all the required software.

And finally, once it works, start experimenting. For example:

  1. Change the WIDTH and HEIGHT variables to change the size of the window
  2. Change the colour values in the line displaysurface.fill((0, 0, 0)) to see what happens

The Game Loop

The Game Loop is a central part of any PyGame platformer game. It’s such an important concept that it’s worth understanding it a bit more.

Let’s break the code from before down bit by bit. Here it is again with lots of comments added to explain what is happening:

# this line imports the PyGame code library
import pygame

# this line initialises the PyGame code
pygame.init()

# this line sets the title text of the window that popups up
pygame.display.set_caption("Platformer")

# these variables control width, height and frame rate
# you can change these to what you need
HEIGHT = 450
WIDTH = 400
FPS = 60

# with a frame rate of 60 FPS the display needs to update 60
# times per second. The computer needs to use a clock to keep # track of all this. Luckily PyGame has a clock built-in, we
# just need to write the code to use it
FramePerSec = pygame.time.Clock()

# all of the shapes in the game - the player, the platforms,
# the enemies - need to be drawn to the screen.PyGame calls it the display. It can be different sizes.
# this line sets the width and height of the display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))

# this is the famous GAME LOOP!
# it will just continue forever until you quit the game
while True:
    # this next 'for' block checks whether you've quit
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

    # every frame the entire game is redrawn
    # this next line fills it with a black colour
    displaysurface.fill((0, 0, 0))
    
    # this next line tells PyGame to update what the user can
    # see on screen
    pygame.display.update()

    # FramePerSec is our game clock - this next line tells
    # PyGame to wait for the next 'tick' of the clock
    # It ticks fast enough to render at 60 FPS
    # When the clock ticks again, the entire game loop
    # runs one more time
    FramePerSec.tick(FPS)

Even if you can’t be bothered to read all the explanations I’d like you to understand this:

  1. The Game Loop is a piece of code that runs again and again, 60 times per second
  2. Each time it runs, the screen is wiped blank, and everything that is in the game is redrawn from scratch

If you imagine the game as drawing on a piece of paper, 60 times per second the computer erases everything that was drawn before and draws the picture again.

Next Steps

The code you have so far is the basic setup code of a PyGame platformer. Whenever you start coding a new game you can copy-paste this code – it’s really general and can be the starting point for all sorts of different games.

Next, continue with Part 2 where we’ll draw the player graphic.

Javascript Code Along: Draw a summer flower

Yes, it’s time for another Javascript Code Along video! In this summery special we draw a flower together.

Sound like fun? Jump in and follow along. It’s a great opportunity for you as a beginner and intermediate programmer to strengthen your coding skills.

Moreover it’s a fun activity that can help you practice. Bored of writing yet another todo-list app? Come and draw some flowers with me. Who knows where it will lead?

While it’s not a Javascript tutorial as such, this video shows you how to:

  • declare variables
  • draw shapes on the HTML canvas element
  • work with coordinate systems
  • work with the quadraticCurveTo() method to create curved shapes

The idea behind this code-along series is that practicing a little every day is a great way to improve your programming skills. Rather than coming up with your own projects you can code along with me. Then experiment and take the code to the next level.

Find all of the code-along Javascript in this public GitHub repository:

https://github.com/nilsm/code-along

Here is the video – enjoy and let me know in the video’s comments if you have any questions!

Javascript Code Along: My Setup Explained

I regularly publish Javascript Code Along videos to help beginners practice their coding skills. A little each day goes a long way after all!

Each of my videos starts with a small piece of code. This post explains what that code is, how it works, and how you can use it yourself to set up a quick and fun project.

As always you can find the full code on GitHub:

https://github.com/nilsm/code-along

Here is the full code to get you started:

<body>
  <canvas id="cv"></canvas>
  <script>
    const el = document.getElementById("cv");

    const w = 600;
    const h = 300;

    /** @type {CanvasRenderingContext2D} */
    const ctx = el.getContext("2d");
    ctx.canvas.width = w;
    ctx.canvas.height = h;

    ctx.beginPath();
    ctx.fillStyle = "#000000";
    ctx.fillRect(0, 0, w, h);
  </script>
</body>

You can copy-paste this into a new file and save it as index.html

When you open it up in the browser it doesn’t look like much. However this simple piece of code provides a great starting point for many fun Javascript adventures.

Let’s walk through the code bit by bit:

The outer body tags

Around the main content you can see the opening/closing <body> tags.

<body>  <----- this is the opening tag
</body> <----- this is the closing tag, with a leading /

These enclose the content of your web page.

The canvas tag

The next element you see is a canvas tag. This is an HTML tag that provides a graphical surface that you can draw on.

I give this canvas tag an id value so that I can access it using Javascript.

The canvas tag also has a corresponding closing tag, just like the body tag before it.

<body>
   <canvas id="cv"></canvas>
</body>

You can find out more about the canvas tag on MDN’s page

The script tag

HTML pages only understand text and images. You will need to tell it when you include Javascript code. You do this using the script tag. Once again it has a corresponding closing tag as well.

<body>
   <canvas id="cv"></canvas>
   <script>
   </script>
</body>

Everything placed inside the script tag must be valid Javascript.

Let’s go through the Javascript code one line at a time.

Using getElementById

The first line uses the built in Javascript method getElementById to get a reference to the canvas tag. It uses the id we gave it earlier on.

A reference is necessary so that you can start to draw on the canvas using Javascript.

const el = document.getElementById("cv");

The variable is called el (short for element) and is declared as a const since it will never change.

Declaring the width and height

The next part of the code stores the width and height of the canvas in two variables:

 const w = 600;
 const h = 300;

This allows us to use the same values in the rest of the code whenever we need them.

By storing the values in variables you make the code easier to maintain. For example it’s easy to change the height of the canvas element by changing the value of h in this single line. The rest of your code will still continue to work.

These are defined as const since their values will never change while the program is running.

Getting the drawing context

In order to draw on the canvas with Javascript you need something called the ‘drawing context’. All the code I write to make animations or draw shapes uses this drawing context. It’s the key method to control the canvas using Javascript.

/** @type {CanvasRenderingContext2D} */
const ctx = el.getContext("2d");

Again it’s a const since it won’t change while the program is running.

The first line looks a bit strange:

/** @type {CanvasRenderingContext2D} */

It’s actually just there to help the code editor I use (Visual Studio Code) to give code completion help for the ctx variable. So it’s useful but you can leave it out if you don’t need it or use a different editor.

You can find out more about the drawing context by watching some of my Javascript Code Along videos on YouTube.

Setting canvas width and height

By default the canvas element has a width of 300 pixels and a height of 150 pixels. I want to control the dimensions with Javascript so that the program knows the exact dimensions.

The width to use is already stored in the variable w and the height in the variable h.

The next two lines set the canvas element on the page to those dimensions.

ctx.canvas.width = w;
ctx.canvas.height = h;

Drawing a background

By default the canvas element has a transparent background. It’s invisible unless you add styling to it, or draw images on it.

To start things off I like to apply a solid background fill using code like this:

ctx.beginPath();
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, w, h);

Let’s look at it in more detail.

ctx.beginPath() tells Javascript that we will start a new drawing on the canvas.

ctx.fillStyle sets the colour to use for the rectangle we’re about to draw. It uses the hexadecimal colour code “#000000” which is black.

ctx.fillRect() draws a rectangle. It goes from coordinate 0, 0 (top left) to the coordinate w, h (full width and height of the canvas, ie. bottom right).

The result is a single black rectangle covering the entire area of the canvas element.

Video

Prefer to watch a video? I go through the code in detail in this clip:

Conclusion

Hopefully that will give you a better understanding of the code I use to start many of my Javascript Code Along videos.

Any questions feel free to ask!

Now it’s time for you to start your own Javascript adventures!

Javascript Code Along: Learn about random numbers

In this Javascript code-along video I show beginner programmers how to work with random numbers. It’s done in a fun way to learn more about these core skills..

Come join me to improve your coding skills!

This video shows you how to:

  • Use Math.random() to generate a random number
  • Generate larger random numbers
  • Generate random integers
  • Pick random elements from a list stored in an array
  • Make random decisions

The idea behind this code-along series is that practicing a little every day is a great way to improve your programming skills. Rather than coming up with your own projects you can code along with me. Then experiment and take the code to the next level.

Find all of the code-along Javascript in this public GitHub repository:

https://github.com/nilsm/code-along

Here is the video – enjoy and let me know in the video’s comments if you have any questions!

Page 1 of 2

Powered by WordPress & Theme by Anders Norén