In PyGame, basic collision detection can be done using pygame.Rect
objects. The Rect
object offers various methods for detecting collisions between objects. Note that even the collision of a rectangular object with a circular object such as a paddle and a ball in Pong game can be roughly detected by a collision between two rectangular objects, the paddle and the bounding rectangle of the ball.
Some examples:
-
pygame.Rect.collidepoint
:Test if a point is inside a rectangle
repl.it/@Rabbid76/PyGame-collidepoint
import pygame pygame.init() window = pygame.display.set_mode((250, 250)) rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(100, 100) run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False point = pygame.mouse.get_pos() collide = rect.collidepoint(point) color = (255, 0, 0) if collide else (255, 255, 255) window.fill(0) pygame.draw.rect(window, color, rect) pygame.display.flip() pygame.quit() exit()
-
pygame.Rect.colliderect
Test if two rectangles overlap
See also How to detect collisions between two rectangular objects or images in pygame
repl.it/@Rabbid76/PyGame-colliderect
import pygame pygame.init() window = pygame.display.set_mode((250, 250)) rect1 = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75) rect2 = pygame.Rect(0, 0, 75, 75) run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False rect2.center = pygame.mouse.get_pos() collide = rect1.colliderect(rect2) color = (255, 0, 0) if collide else (255, 255, 255) window.fill(0) pygame.draw.rect(window, color, rect1) pygame.draw.rect(window, (0, 255, 0), rect2, 6, 1) pygame.display.flip() pygame.quit() exit()
Furthermore pygame.Rect.collidelist
and pygame.Rect.collidelistall
can be used for the collision test between a rectangle and a list of rectangles. pygame.Rect.collidedict
and pygame.Rect.collidedictall
can be used for the collision collision test between a rectangle and a dictionary of rectangles.
The collision of pygame.sprite.Sprite
and pygame.sprite.Group
objects, can be detected by pygame.sprite.spritecollide()
, pygame.sprite.groupcollide()
or pygame.sprite.spritecollideany()
. When using these methods, the collision detection algorithm can be specified by the collided
argument:
The collided argument is a callback function used to calculate if two sprites are colliding.
Possible collided
callables are collide_rect
, collide_rect_ratio
, collide_circle
, collide_circle_ratio
, collide_mask
Some examples:
-
pygame.sprite.spritecollide()
repl.it/@Rabbid76/PyGame-spritecollide
import pygame pygame.init() window = pygame.display.set_mode((250, 250)) sprite1 = pygame.sprite.Sprite() sprite1.image = pygame.Surface((75, 75)) sprite1.image.fill((255, 0, 0)) sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75) sprite2 = pygame.sprite.Sprite() sprite2.image = pygame.Surface((75, 75)) sprite2.image.fill((0, 255, 0)) sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75) all_group = pygame.sprite.Group([sprite2, sprite1]) test_group = pygame.sprite.Group(sprite2) run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False sprite1.rect.center = pygame.mouse.get_pos() collide = pygame.sprite.spritecollide(sprite1, test_group, False) window.fill(0) all_group.draw(window) for s in collide: pygame.draw.rect(window, (255, 255, 255), s.rect, 5, 1) pygame.display.flip() pygame.quit() exit()
For a collision with masks see How can I made a collision mask? or Pygame mask collision
See also Collision and Intersection
-
pygame.sprite.spritecollide()
/collide_circle
repl.it/@Rabbid76/PyGame-spritecollidecollidecircle
import pygame pygame.init() window = pygame.display.set_mode((250, 250)) sprite1 = pygame.sprite.Sprite() sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA) pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40) sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(40, 40) sprite2 = pygame.sprite.Sprite() sprite2.image = pygame.Surface((80, 89), pygame.SRCALPHA) pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40) sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80) all_group = pygame.sprite.Group([sprite2, sprite1]) test_group = pygame.sprite.Group(sprite2) run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False sprite1.rect.center = pygame.mouse.get_pos() collide = pygame.sprite.spritecollide(sprite1, test_group, False, pygame.sprite.collide_circle) window.fill(0) all_group.draw(window) for s in collide: pygame.draw.circle(window, (255, 255, 255), s.rect.center, s.rect.width // 2, 5) pygame.display.flip() pygame.quit() exit()
What does this all mean for your code?
pygame.Surface.get_rect.get_rect()
returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. The position of the rectangle can be specified by a keyword argument. For example, the center of the rectangle can be specified with the keyword argument center
. These keyword argument are applied to the attributes of the pygame.Rect
before it is returned (see pygame.Rect
for a full list of the keyword arguments).
See *Why is my collision test always returning ‘true’ and why is the position of the rectangle of the image always wrong (0, 0)?
You don’t need the x
and y
attributes of Sprite
and Bullet
at all. Use the position of the rect
attribute instead:
#Define the sprite class
class Sprite:
def __init__(self, x, y, name):
self.image = pygame.image.load(name)
self.rect = self.image.get_rect(topleft = (x, y))
def render(self):
window.blit(self.image, self.rect)
# Define the bullet class to create bullets
class Bullet:
def __init__(self, x, y):
self.bullet = pygame.image.load("user_bullet.BMP")
self.rect = self.bullet.get_rect(topleft = (x + 23, y))
def render(self):
window.blit(self.bullet, self.rect)
Use pygame.Rect.colliderect()
to detect collisions between instances of Sprite
and Bullet
.
See How to detect collisions between two rectangular objects or images in pygame:
my_sprite = Sprite(sx, sy, name)
my_bullet = Bullet(by, by)
while True:
# [...]
if my_sprite.rect.colliderect(my_bullet.rect):
printe("hit")