Skip to main content

# Rect Collision Response

A simple demo on how to do side-based rect collision response.

pymike
(pymike)
This is a simple demo on how to do side-based rectangle collision response. It's extremely simple, and not very hard to understand.

### Concept

I'm putting this here since it's not in the download. Basically how it works is when you move a rect, you first move along the X axis, test for a collision, move out, then move along the Y axis, test for a collision, and move out. This prevents the infamous "corner-catching" bug, and lets you move smoothly along walls.

```#! /usr/bin/env python

import os
import random
import pygame

# Class for the orange dude
class Player(object):

def __init__(self):
self.rect = pygame.Rect(32, 32, 16, 16)

def move(self, dx, dy):

# Move each axis separately. Note that this checks for collisions both times.
if dx != 0:
self.move_single_axis(dx, 0)
if dy != 0:
self.move_single_axis(0, dy)

def move_single_axis(self, dx, dy):

# Move the rect
self.rect.x += dx
self.rect.y += dy

# If you collide with a wall, move out based on velocity
for wall in walls:
if self.rect.colliderect(wall.rect):
if dx > 0: # Moving right; Hit the left side of the wall
self.rect.right = wall.rect.left
if dx < 0: # Moving left; Hit the right side of the wall
self.rect.left = wall.rect.right
if dy > 0: # Moving down; Hit the top side of the wall
self.rect.bottom = wall.rect.top
if dy < 0: # Moving up; Hit the bottom side of the wall
self.rect.top = wall.rect.bottom

# Nice class to hold a wall rect
class Wall(object):

def __init__(self, pos):
walls.append(self)
self.rect = pygame.Rect(pos[0], pos[1], 16, 16)

# Initialise pygame
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()

# Set up the display
pygame.display.set_caption("Get to the red square!")
screen = pygame.display.set_mode((320, 240))

clock = pygame.time.Clock()
walls = [] # List to hold the walls
player = Player() # Create the player

# Holds the level layout in a list of strings.
level = [
"WWWWWWWWWWWWWWWWWWWW",
"W                  W",
"W         WWWWWW   W",
"W   WWWW       W   W",
"W   W        WWWW  W",
"W WWW  WWWW        W",
"W   W     W W      W",
"W   W     W   WWW WW",
"W   WWW WWW   W W  W",
"W     W   W   W W  W",
"WWW   W   WWWWW W  W",
"W W      WW        W",
"W W   WWWW   WWW   W",
"W     W    E   W   W",
"WWWWWWWWWWWWWWWWWWWW",
]

# Parse the level string above. W = wall, E = exit
x = y = 0
for row in level:
for col in row:
if col == "W":
Wall((x, y))
if col == "E":
end_rect = pygame.Rect(x, y, 16, 16)
x += 16
y += 16
x = 0

running = True
while running:

clock.tick(60)

for e in pygame.event.get():
if e.type == pygame.QUIT:
running = False
if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE:
running = False

# Move the player if an arrow key is pressed
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
player.move(-2, 0)
if key[pygame.K_RIGHT]:
player.move(2, 0)
if key[pygame.K_UP]:
player.move(0, -2)
if key[pygame.K_DOWN]:
player.move(0, 2)

# Just added this to make it slightly fun ;)
if player.rect.colliderect(end_rect):
raise SystemExit, "You win!"

# Draw the scene
screen.fill((0, 0, 0))
for wall in walls:
pygame.draw.rect(screen, (255, 255, 255), wall.rect)
pygame.draw.rect(screen, (255, 0, 0), end_rect)
pygame.draw.rect(screen, (255, 200, 0), player.rect)
pygame.display.flip()
```

### Links

Home Page
http://pymike.pynguins.com/?page=tutorials&id=1

### Pygame.org account Comments

• #### Sillyurs 2011-08-26 13:54:12

The link is broken...

swag on u

• #### CHAD2430 2014-06-12 15:29:48

Used the collision detection code on my own project, but the block warps outside the walls provided and is invisible.

• #### Duality 2014-09-16 21:54:31

and how do you do the collsion detection part without using the rect class ? i am trying to understand how this all works.

• #### piglet coder 2015-09-10 00:02:13

How do I use sprites for the walls and player instead of solid colors?

#### The Orca 2015-09-11 06:25:56

You'd just blit the sprites into place rather than drawing rects for them. For example, where it says:
pygame.draw.rect(screen, (255, 200, 0), player.rect)
You could replace that with:
screen.blit(sprite, player_x_coordinate, player_y_coordinate)

You'd naturally need to make sure the player's x and y coordinates are tracked well. Also, make sure to convert the sprite first. While you can just put the file you wish to use as an image there, it will be extremely slow if not converted outside your game loop.

#### piglet coder 2015-11-12 23:25:26

Oh cool thank you☻☺☻☺☻☺☻☺☻☺☻☺!..
I'll do something like this.
|
\/

#### es_04 2018-05-28 17:09:53.656128

Could you please post the code for sprites in walls and player here?

lol

• #### James Witherspoon 2015-10-07 18:57:17

Hi there, what version of pygame and python is this using? Thanls

• #### JBalisticMC 2015-10-24 13:25:47

bif="bg.jpg"
mif="Untitled.png"
pot="Untitled23.png"
lol="pin.png"

red = (255,0,0)

import pygame, sys, random, time, os
from pygame.locals import *

AppleThinkness = 30
block_size = 20
FPS = 15

appleimg = pygame.image.load('apple.png')

pygame.init()
screen=pygame.display.set_mode((700,400),0,32) #Sets screen size to 640 x 360 @ 32 bit
pygame.display.set_caption('Frantic Balloon Pops')

icon = pygame.image.load('Untitled-icon.png')
pygame.display.set_icon(icon)

ball=pygame.image.load(mif).convert_alpha()
background=pygame.image.load(bif).convert()
pin=pygame.image.load(lol).convert_alpha()

smallfont = pygame.font.SysFont("comicsansms", 25)
medfont = pygame.font.SysFont("comicsansms", 50)
largefont = pygame.font.SysFont("comicsansms", 80)

lead_x = 700/2
lead_y = 400/2

lead_x_change = 10
lead_y_change = 0

lead_x += lead_x_change
lead_y += lead_y_change

def score(score):
text = smallfont.render("Score: "+str(score), True, red)
screen.blit(text, [0,0])

SPAWN_LOCATIONS = []
def spawn_balls():
# Don't have this in the loop as you only want them to spawn once when the game starts, not every time the game loops.
NUMBER_OF_BALLS_TO_SPAWN = 300
for i in range(0,NUMBER_OF_BALLS_TO_SPAWN):
# This will happen the number of times equal to NUMBER_OF_BALLS_TO_SPAWN at the moment 6.

# Generate a random x position, between 0 and 640 (Screen width.)
x_pos_ball = random.randint(0,700-54) # 54 is the size of the ball image, adust end points so they are on screen.
# Generate a random y position, between 0 and 360 (Screen height.)
y_pos_ball = random.randint(0,400-54)

# Draw the ball at the random position
SPAWN_LOCATIONS.append((x_pos_ball,y_pos_ball))

def text_objects(text,color,size):
if size == "small":
textSurface = smallfont.render(text, True, color)
elif size == "medium":
textSurface = medfont.render(text, True, color)
elif size == "large":
textSurface = largefont.render(text, True, color)

def message_to_screen(msg,color, y_displace=0, size = "small"):
textSurf, textRect = text_objects(msg, color, size)
#screen_text = font.render(msg, True, color)
#gameDisplay.blit(screen_text, [display_width/2, display_height/2])
textRect.center = (400 / 2),(700 / 2)+y_displace
screen.blit(textSurf, textRect)

# SPawn all the balls.
spawn_balls()
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.blit(background, (0,0))

score (0)

# Commented out all this stuff as we dont't need it for random spawning.
"""# so this is where you list all of the locations for each circle...
spawn_locations = [(0,0),(50,50),(100,100),(150,150),(200,200),
(250,250),(300,300),(350,350),(400,400),(0,1),
(0,50),(0,100),(0,150),(0,200),(0,250),(0,300),(0,350),
(0,-1),(0,-50),(0,-100),(0,-150),(50,0),(100,0),(150,0),(200,0),
(250,0),(300,0),(350,0),(400,0),(450,0),(500,0),(550,0),(600,0),(650,0),(700,0),
(750,0),]

# so it is just like the fruits where fruits = ["apple", "pear", "some other fruit..."] but instead
# "apple" is changed for (0,0) which is the coordinate for where you create the circle
# and this will go through each location i.e (0,0) and blit to the screen a cirle at that location
# so all you have to do to add more is add more locations to the spawn_locations list"""
for spawn_location in SPAWN_LOCATIONS:
screen.blit(ball, spawn_location)

x,y = pygame.mouse.get_pos()
x -= pin.get_width()/2
y -= pin.get_height()/2

screen.blit(pin,(x,y))

pygame.display.update()
-----------------------------------------------------------------------------------------------------------------
I want when the 'pin' touches the 'balloon' than it will pop. How do I do this? and what do I add?

• #### Max 2016-01-09 16:19:06

Thank you very much :-)

• #### xXMartinBauzaXx 2016-05-18 23:49:55

if you are here that means than you actually have pygame so copy the source code and
paste it!!!

• #### Pavel stanley 2016-08-15 13:31:12

This tut helped me a lot. Thank you very much.

very nice