Collisions

Pygame, unlike game engines like Unity or Godot, doesn’t have concept of game objects. Images are handled through concept of surface, a set of pixels. To position surfaces on screen you already have used Rect objects. Besides ability to position objects on a screen Rect also provides methods to check various collisions.

Here you will be presented two most common types of check for collision. A collision between Rect and a point and collision between two Rect objects.

Point collision

First collision type is to check does a point collide with a rect. This is useful when you want to check for example was mouse clicked within an on object on a screen. Next you will learn how to detect mouse click within a very simple button.

Type in the following initial program and save it to the file:

import pygame as pg

pg.init()
screen = pg.display.set_mode([500, 500])

# Button (green)
button = pg.Surface((150, 80))
button.fill((0, 255, 0))
button_rect = button.get_rect(center=(250, 250))

# Pressed button
pressed_button = pg.Surface((150, 80))
pressed_button.fill((255, 0, 0))

while True:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            exit()

    draw_button = button
    if pg.mouse.get_pressed()[0]:
        draw_button = pressed_button

    screen.fill((0, 0, 0))
    screen.blit(draw_button, button_rect)
    pg.display.flip()

When you run the program you see that when ever you click a mouse button color of button changes from green to red. But color changes when clicking anywhere. To change that so that color changes only when you click on the button you need to do very minimal change to the code:

@@ 21 @@
     if pg.mouse.get_pressed()[0]:
-        draw_button = pressed_button
+        if button_rect.collidepoint(pg.mouse.get_pos()):
+            draw_button = pressed_button

And that’s it. collidepoint method on a Rect object checks if the given point is within the rect.

Note

Right edge and bottom edge are not considered to be inside the rect.

The following is the full code:

import pygame as pg

pg.init()
screen = pg.display.set_mode([500, 500])

# Button (green)
button = pg.Surface((150, 80))
button.fill((0, 255, 0))
button_rect = button.get_rect(center=(250, 250))

# Pressed button
pressed_button = pg.Surface((150, 80))
pressed_button.fill((255, 0, 0))

while True:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            exit()

    draw_button = button
    if pg.mouse.get_pressed()[0]:
        if button_rect.collidepoint(pg.mouse.get_pos()):
            draw_button = pressed_button

    screen.fill((0, 0, 0))
    screen.blit(draw_button, button_rect)
    pg.display.flip()

Colliding to another rect

Second collision type is to check does two Rect objects collide to each other. This is very useful for all kind of checks for example to check if player collided with an enemy.

This is the initial code. Type it in and save to a file:

import pygame as pg

pg.init()
screen = pg.display.set_mode([500, 500])

# Dangerzone
dangerzone = pg.Surface((150, 80))
dangerzone.fill((255, 0, 0))
dangerzone_rect = dangerzone.get_rect(center=(250, 250))

# Player
player = pg.Surface((50, 50))
player.fill((0, 0, 255))
player_rect = player.get_rect(topleft=(10, 10))

clock = pg.time.Clock()

while True:
    clock.tick(30)

    for event in pg.event.get():
        if event.type == pg.QUIT:
            exit()

    keys = pg.key.get_pressed()

    if keys[pg.K_w]:
        player_rect.y -= 3
    if keys[pg.K_a]:
        player_rect.x -= 3
    if keys[pg.K_s]:
        player_rect.y += 3
    if keys[pg.K_d]:
        player_rect.x += 3

    screen.fill((0, 0, 0))
    screen.blit(dangerzone, dangerzone_rect)
    screen.blit(player, player_rect)
    pg.display.flip()

In this example you can move around by using classical WASD-keys movement. On screen you see two objects smaller blue rectangle at upper left corner and big red rectangle at middle of the screen. Blue is you - the player and red is “danger zone” which you’re not supposed to move on to. Initially you can move freely around the screen and when you go over red area nothing happens.

Now change the program to react red area in the screen and reset player position back to top left corner if player touches the red area. To achieve that do the following changes to your code:

@@ 34 @@
        player_rect.left += 3
+
+    if player_rect.colliderect(dangerzone_rect):
+        player_rect.topleft = (10, 10)

The colliderect method on Rect object checks if those two rects overlap. If they do it returns True and False if they don’t. This is useful to check collisions between any objects.

Note

This is very simple check and doesn’t take into account shapes that are complex.

The following is the full code:

import pygame as pg

pg.init()
screen = pg.display.set_mode([500, 500])

# Dangerzone
dangerzone = pg.Surface((150, 80))
dangerzone.fill((255, 0, 0))
dangerzone_rect = dangerzone.get_rect(center=(250, 250))

# Player
player = pg.Surface((50, 50))
player.fill((0, 0, 255))
player_rect = player.get_rect(topleft=(10, 10))

clock = pg.time.Clock()

while True:
    clock.tick(30)

    for event in pg.event.get():
        if event.type == pg.QUIT:
            exit()

    keys = pg.key.get_pressed()

    if keys[pg.K_w]:
        player_rect.top -= 3
    if keys[pg.K_a]:
        player_rect.left -= 3
    if keys[pg.K_s]:
        player_rect.top += 3
    if keys[pg.K_d]:
        player_rect.left += 3

    if player_rect.colliderect(dangerzone_rect):
        player_rect.topleft = (10, 10)

    screen.fill((0, 0, 0))
    screen.blit(dangerzone, dangerzone_rect)
    screen.blit(player, player_rect)
    pg.display.flip()

Summary

Congratulations! In this section you have learned how to detect collisions in two ways:

  • Point within a rect

  • Rect overlapping an another rect