Introduction To Collision
Most pygame projects will require some form of collision detection and handling. This can be done manually by checking for an overlap between the objects, but fortunately pygame has built in functionality for collisions.
Rectangle Collisions
The most common collision method in pygame is using rectangles. In previous tutorial we looked at how rectangles are used to position and move objects on the screen, well they are also used for collision detection.
But first of all let’s look at how a rectangle collision check works. Detecting a collision is a case of checking to see if any of the edges between two rectangles intersect. This will result in an overlapping collision region.
Luckily we don’t have to do this manually as pygame has a built in method for checking collision between rectangles, which is .colliderect()
.
The code below demonstrates this collision check. Run the code and you will see a green rectangle that you can move with the mouse and a blue rectangle, which is the obstacle. When the two rectangles collide, the main rectangle changes colour.
I’ve added comments throughout this code to help explain what the different parts do.
import pygame
import random
pygame.init()
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Collision")
#create main rectangle & obstacle rectangle
rect_1 = pygame.Rect(0, 0, 25, 25)
obstacle_rect = pygame.Rect(random.randint(0, 500), random.randint(0, 300), 25, 25)
#define colours
BG = (50, 50, 50)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
#hide mouse cursor
pygame.mouse.set_visible(False)
run = True
while run:
#update background
screen.fill(BG)
#check collision and change colour
col = GREEN
if rect_1.colliderect(obstacle_rect):
col = RED
#get mouse coordinates and use them to position the rectangle
pos = pygame.mouse.get_pos()
rect_1.center = pos
#draw both rectangles
pygame.draw.rect(screen, col, rect_1)
pygame.draw.rect(screen, BLUE, obstacle_rect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.flip()
pygame.quit()
Collision With Multiple Rectangles
In a typical game you will have a lot more than just two rectangles though, so let’s see how to handle multiple collision checks. We will modify the above code to create a bunch of rectangles and add them to a list, then iterate through the list and check for collision with each rectangle.
import pygame
import random
pygame.init()
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Collision")
#create main rectangle
rect_1 = pygame.Rect(0, 0, 25, 25)
#create empty list, then create 16 obstacle rectangles using a loop and add to list
obstacles = []
for _ in range(16):
obstacle_rect = pygame.Rect(random.randint(0, 500), random.randint(0, 300), 25, 25)
obstacles.append(obstacle_rect)
#define colours
BG = (50, 50, 50)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
#hide mouse cursor
pygame.mouse.set_visible(False)
run = True
while run:
#update background
screen.fill(BG)
#check collision and change colour
col = GREEN
for obstacle in obstacles:
if rect_1.colliderect(obstacle):
col = RED
#get mouse coordinates and use them to position the rectangle
pos = pygame.mouse.get_pos()
rect_1.center = pos
#draw all rectangles
pygame.draw.rect(screen, col, rect_1)
for obstacle in obstacles:
pygame.draw.rect(screen, BLUE, obstacle)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.flip()
pygame.quit()
The result will be similar to before, but now there are many more obstacle rectangles. The player rectangle remains green until it collides with any of the obstacles, then it changes to red.
Using .collidelist()
It is possible to refine this code further since pygame has a build in function for checking for collision with a list of rectangles. This works the same way as before, but it saves us manually iterating through the list.
To do this, we use .collidelist()
and pass the list of obstacle rectangles into it. This will return the index of the first collision rectangle. If there is no collision, it will return -1
.
The code below is almost identical to the previous code block, but it now uses the new method on line 37. The if
statement checks that the value is not -1
, which confirms a collision has been detected. It then changes the colour of the player rectangle to red as before, but it also prints
out the index value of the collision rectangle. Try out the code and see the different values being printed out in the console as you collide with different rectangles.
import pygame
import random
pygame.init()
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Collision")
#create main rectangle
rect_1 = pygame.Rect(0, 0, 25, 25)
#create empty list, then create 16 obstacle rectangles using a loop and add to list
obstacles = []
for _ in range(16):
obstacle_rect = pygame.Rect(random.randint(0, 500), random.randint(0, 300), 25, 25)
obstacles.append(obstacle_rect)
#define colours
BG = (50, 50, 50)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
#hide mouse cursor
pygame.mouse.set_visible(False)
run = True
while run:
#update background
screen.fill(BG)
#check collision and change colour
col = GREEN
if rect_1.collidelist(obstacles) >= 0:
print(rect_1.collidelist(obstacles))
col = RED
#get mouse coordinates and use them to position the rectangle
pos = pygame.mouse.get_pos()
rect_1.center = pos
#draw all rectangles
pygame.draw.rect(screen, col, rect_1)
for obstacle in obstacles:
pygame.draw.rect(screen, BLUE, obstacle)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#update display
pygame.display.flip()
pygame.quit()
Point Collisions
Another useful collision check is .collidepoint()
. This function takes a set of x & y coordinates as an argument and will check for a collision between those coordinates and the rectangle it is being used on. This is very useful for applications that require mouse input such as clicking on an object or clicking on buttons.
To demonstrate this, I will use the same random rectangles from before but this time I won’t attach a rectangle to the mouse. Instead I will use the mouse coordinates and check collision with each rectangle.
import pygame
import random
pygame.init()
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Collision")
#create empty list, then create 16 obstacle rectangles using a loop and add to list
obstacles = []
for _ in range(16):
obstacle_rect = pygame.Rect(random.randint(0, 500), random.randint(0, 300), 25, 25)
obstacles.append(obstacle_rect)
#define colours
BG = (50, 50, 50)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
run = True
while run:
#update background
screen.fill(BG)
#check collision and change colour
pos = pygame.mouse.get_pos()
for obstacle in obstacles:
if obstacle.collidepoint(pos):
pygame.draw.rect(screen, RED, obstacle)
else:
pygame.draw.rect(screen, GREEN, obstacle)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.flip()
pygame.quit()
The result of the above code should be similar to before. A bunch of randomly positioned rectangles, which are green by default. Hovering the mouse over any of the rectangles, detects a collision and changes that rectangle colour to red.
Line Collisions
The third and final collision check in this section is a little different to the ones we looked at so far. This one checks for a collision between a rectangle and a line. The screenshot below shows it in action. Any rectangles that the line passes through change colour to red.
This is something I have used in my projects to check for “line of sight” between an enemy and a player. I draw a line from each enemy to the player. Then check for collision with obstacles, which allows me to determine if the enemies can see the player or if there are obstacles in the way that hide the player.
To carry out this collision check we use the .clipline() method, which takes the line start and end coordinates as arguments. If the line clips through the rectangle, this returns a tuple with the line coordinates, otherwise it returns an empty tuple.
We can then use these returned values as a way of checking if the line has intersected the rectangle or not. Try out the code below to see how this method works.
import pygame
import random
pygame.init()
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 400
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Collision")
#create empty list, then create 16 obstacle rectangles using a loop and add to list
obstacles = []
for _ in range(16):
obstacle_rect = pygame.Rect(random.randint(0, 500), random.randint(0, 300), 25, 25)
obstacles.append(obstacle_rect)
#define line start point as the center of the screen
line_start = (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2)
#define colours
BG = (50, 50, 50)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
run = True
while run:
#update background
screen.fill(BG)
#check mouse coordinates and draw a line to it
pos = pygame.mouse.get_pos()
pygame.draw.line(screen, WHITE, line_start, pos, 5)
#check for line collision with each rectangle
for obstacle in obstacles:
if obstacle.clipline((line_start, pos)):
pygame.draw.rect(screen, RED, obstacle)
else:
pygame.draw.rect(screen, GREEN, obstacle)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.flip()
pygame.quit()
Really awesome tutorials..
Thank you Sir….
Thank you sir, that is exactly the kind of tutorial that I need.
Best