Collision Detection In Pygame By albro

Collision Detection In Pygame

In this section, we want to work a little more with rectangles and move them with the mouse and keyboard, and learn more about the Rect object.

Working with rectangles

A rectangle is a very useful object in graphics programming. The Rect class in Pygame is used to work with a rectangular area. A Rect object of the Rect class can be created by having the following:

  • 4 left, top, width and height parameters
  • Position and size
  • An object that has the rect attribute
Rect(left, top, width, height)
Rect(pos, size)
Rect(obj)

Methods that change position or size, such as move and inflate, leave the original rectangle intact and return a new rectangle.

Other properties of the rectangle

The Rect object also has other properties that can be used to move and align the rectangle. Applying these features does not change the size of the rectangle. These features are listed below:

x, y
top, left, bottom, right
topleft, bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery

Using the following five properties will change the size of the rectangle while maintaining its top left position:

size, width, height, w, h

The following code displays the features introduced above in the console:

import pygame
from pygame.locals import *
SIZE = 500, 200
RED = (255, 0, 0)
GRAY = (150, 150, 150)
pygame.init()
screen = pygame.display.set_mode(SIZE)
rect = Rect(50, 60, 200, 80)
print(f'x={rect.x}, y={rect.y}, w={rect.w}, h={rect.h}')
print(f'left={rect.left}, top={rect.top}, right={rect.right}, bottom={rect.bottom}')
print(f'center={rect.center}')
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
    screen.fill(GRAY)
    pygame.draw.rect(screen, RED, rect)
    pygame.display.flip()
pygame.quit()

Special points

The Rect class has four side points, four middle points and a center point.

special points

Below is the code for drawing the rectangle above:

from rect import *
pts = ('topleft', 'topright', 'bottomleft', 'bottomright','midtop', 'midright', 'midbottom', 'midleft', 'center')
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
    screen.fill(GRAY)
    pygame.draw.rect(screen, GREEN, rect, 4)
    for pt in pts:
        pos = eval('rect.'+pt)
        draw_text(pt, pos)
        pygame.draw.circle(screen, RED, pos, 3)
    pygame.display.flip()
pygame.quit()

Horizontal and vertical alignment

In the following example, we use three keys to align the rectangle horizontally:

  • L - left
  • C - center
  • R - right

And we use the following three other keys for the vertical alignment of the rectangle:

  • T - top
  • M - middle
  • B - bottom

alignment

from rect import *
rect = Rect(50, 60, 200, 80)
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
if event.type == KEYDOWN: if event.key == K_l: rect.left = 0 if event.key == K_c: rect.centerx = width//2 if event.key == K_r: rect.right = width
if event.key == K_t: rect.top = 0 if event.key == K_m: rect.centery = height//2 if event.key == K_b: rect.bottom = height
screen.fill(GRAY) pygame.draw.rect(screen, BLUE, rect) pygame.display.flip()
pygame.quit()

Move the rectangle with the keyboard

The move(v) method creates a new rectangle moved by the vector v. The move_ip(v) method moves a rectangle in place. The following program moves a rectangle around using the arrow keys. The thin blue rectangle is the original rectangle, and the red rectangle is the blue rectangle that has moved. We use a dictionary to associate a motion vector for each of the 4 arrow keys. We move 5 pixels in any direction:

dir = {K_LEFT: (-5, 0), K_RIGHT: (5, 0), K_UP: (0, -5), K_DOWN: (0, 5)}

move rectangle

from rect import *
rect0 = Rect(50, 60, 200, 80) rect = rect0.copy()
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
if event.type == KEYDOWN: if event.key in dir: v = dir[event.key] rect.move_ip(v)
screen.fill(GRAY) pygame.draw.rect(screen, BLUE, rect0, 1) pygame.draw.rect(screen, RED, rect, 4) pygame.display.flip()
pygame.quit()

Change the size of the rectangle

The inflate(v) method enlarges or shrinks a rectangle by the vector v and creates a new rectangle. The inflate_ip(v) method causes a rectangle to grow or shrink in place. The following program changes the size of the rectangle using the arrow keys. The modified red rectangle is the blue rectangle.

inflate rectangle

from rect import *
rect0 = rect.copy()
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
if event.type == KEYDOWN: if event.key in dir: v = dir[event.key] rect.inflate_ip(v)
screen.fill(GRAY) pygame.draw.rect(screen, BLUE, rect0, 1) pygame.draw.rect(screen, RED, rect, 4) pygame.display.flip()
pygame.quit()

Move the rectangle with the mouse

If the point collides with the rectangle, the rect.collidepoint(pos) function returns a Boolean value of True. We use this return value along with the mouse position to find out if the mouse has clicked inside the rectangle or not. If the click has occurred, we move the rectangle with the mouse. The variable moving is set when the mouse button is clicked inside the rectangle. This variable remains true until the button is not raised. The rectangle moves only when the mouse is clicked inside the rectangle. While the rectangle is moving, add a blue border around the rectangle as shown below:

move with mouse

from rect import *
moving = False
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
elif event.type == MOUSEBUTTONDOWN: if rect.collidepoint(event.pos): moving = True
elif event.type == MOUSEBUTTONUP: moving = False
elif event.type == MOUSEMOTION and moving: rect.move_ip(event.rel)
screen.fill(GRAY) pygame.draw.rect(screen, RED, rect) if moving: pygame.draw.rect(screen, BLUE, rect, 4) pygame.display.flip()
pygame.quit()

Self-propelled rectangle

The following code moves a rectangle by the amount v :

rect.move_ip(v)

It then checks the four borders of the rectangle and sets the speed at which the rectangle moves outside the application window.

Self-propelled rectangle

from rect import *
rect = Rect(100, 50, 50, 50) v = [2, 2]
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
rect.move_ip(v)
if rect.left < 0: v[0] *= -1 if rect.right > width: v[0] *= -1 if rect.top < 0: v[1] *= -1 if rect.bottom > height: v[1] *= -1
screen.fill(GRAY) pygame.draw.rect(screen, RED, rect) pygame.display.flip()
pygame.quit()

colliding points

The rect.collidepoint(p) method checks whether a rectangle collides with point p or not. In the program below, we create a hundred random points and if they are inside the rectangle, we color them red. Each time you press the R key, a hundred new random points are generated.

colliding points

from rect import *
points = random_points(100)
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
if event.type == KEYDOWN: if event.key == K_r: points = random_points(100)
screen.fill(GRAY) pygame.draw.rect(screen, GREEN, rect, 1) for p in points: if rect.collidepoint(p): pygame.draw.circle(screen, RED, p, 4, 0) else: pygame.draw.circle(screen, BLUE, p, 4, 0)
pygame.display.flip()
pygame.quit()

Colliding rectangles

The rect.colliderect(r) method checks whether a rectangle collides with another rectangle. In the program below, we create 50 random rectangles, and if we encounter a green rectangle, we color them red. Each time you press the R key, a hundred new random rectangles are created.

Colliding rectangles

from rect import *
n = 50 rects = random_rects(n)
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
if event.type == KEYDOWN: if event.key == K_r: rects = random_rects(n)
screen.fill(GRAY) pygame.draw.rect(screen, GREEN, rect, 1)
for r in rects: if rect.colliderect(r): pygame.draw.rect(screen, RED, r, 2) else: pygame.draw.rect(screen, BLUE, r, 1)
pygame.display.flip()
pygame.quit()

Overlapping rectangles

The rect.colliderect(r) method checks whether a rectangle collides with another rectangle or not. If we want to know that two rectangles overlap, we have to compare both rectangles with each other. The number of comparisons increases exponentially. Each time the R key is pressed, 20 new random rectangles are generated.

Overlapping rectangles

from rect import *
n = 30 rects = random_rects(n)
while running: for event in pygame.event.get(): if event.type == QUIT: running = False
if event.type == KEYDOWN: if event.key == K_r: rects = random_rects(n)
screen.fill(GRAY)
intersecting = [] for i in range(n-1): r0 = rects[i] for j in range(i+1, n): r1 = rects[j] if r0.colliderect(r1): intersecting.append(r0) intersecting.append(r1) break
for i, r in enumerate(rects): color = RED if r in intersecting else BLUE pygame.draw.rect(screen, color, r) draw_text(str(i), r.topleft)
pygame.display.flip()
pygame.quit()