How do I detect collision in pygame?

Posted on

Solving problem is about exposing yourself to as many situations as possible like How do I detect collision in pygame? and practice these strategies over and over. With time, it becomes second nature and a natural way you approach any problems in general. Big or small, always start with a plan, use other strategies mentioned here till you are confident and ready to code the solution.
In this post, my aim is to share an overview the topic about How do I detect collision in pygame?, which can be followed any time. Take easy to follow this discuss.

How do I detect collision in pygame?

I have made a list of bullets and a list of sprites using the classes below. How do I detect if a bullet collides with a sprite and then delete that sprite and the bullet?

#Define the sprite class
class Sprite:
    def __init__(self,x,y, name):
        self.x=x
        self.y=y
        self.image = pygame.image.load(name)
        self.rect = self.image.get_rect()
    def render(self):
        window.blit(self.image, (self.x,self.y))
# Define the bullet class to create bullets          
class Bullet:
    def __init__(self,x,y):
        self.x = x + 23
        self.y = y
        self.bullet = pygame.image.load("user_bullet.BMP")
        self.rect = self.bullet.get_rect()
    def render(self):
        window.blit(self.bullet, (self.x, self.y))

Answer #1:

From what I understand of pygame you just need to check if the two rectangles overlap using the colliderect method. One way to do it is to have a method in your Bullet class that checks for collisions:

def is_collided_with(self, sprite):
    return self.rect.colliderect(sprite.rect)

Then you can call it like:

sprite = Sprite(10, 10, 'my_sprite')
bullet = Bullet(20, 10)
if bullet.is_collided_with(sprite):
    print('collision!')
    bullet.kill()
    sprite.kill()
Answered By: 101

Answer #2:

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

    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")
Answered By: Rabbid76

Answer #3:

There is a very simple method for what you are trying to do using built in methods.

here is an example.

import pygame
import sys
class Sprite(pygame.sprite.Sprite):
    def __init__(self, pos):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([20, 20])
        self.image.fill((255, 0, 0))
        self.rect = self.image.get_rect()
        self.rect.center = pos
def main():
    pygame.init()
    clock = pygame.time.Clock()
    fps = 50
    bg = [255, 255, 255]
    size =[200, 200]
    screen = pygame.display.set_mode(size)
    player = Sprite([40, 50])
    player.move = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN]
    player.vx = 5
    player.vy = 5
    wall = Sprite([100, 60])
    wall_group = pygame.sprite.Group()
    wall_group.add(wall)
    player_group = pygame.sprite.Group()
    player_group.add(player)
    # I added loop for a better exit from the game
    loop = 1
    while loop:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                loop = 0
        key = pygame.key.get_pressed()
        for i in range(2):
            if key[player.move[i]]:
                player.rect.x += player.vx * [-1, 1][i]
        for i in range(2):
            if key[player.move[2:4][i]]:
                player.rect.y += player.vy * [-1, 1][i]
        screen.fill(bg)
        # first parameter takes a single sprite
        # second parameter takes sprite groups
        # third parameter is a do kill command if true
        # all group objects colliding with the first parameter object will be
        # destroyed. The first parameter could be bullets and the second one
        # targets although the bullet is not destroyed but can be done with
        # simple trick bellow
        hit = pygame.sprite.spritecollide(player, wall_group, True)
        if hit:
            # if collision is detected call a function in your case destroy
            # bullet
            player.image.fill((255, 255, 255))
        player_group.draw(screen)
        wall_group.draw(screen)
        pygame.display.update()
        clock.tick(fps)
    pygame.quit()
    # sys.exit
if __name__ == '__main__':
    main()
Answered By: Nex_Lite

Answer #4:

Make a group for the bullets, and then add the bullets to the group.

What I would do is this:
In the class for the player:

def collideWithBullet(self):
    if pygame.sprite.spritecollideany(self, 'groupName'):
        print("CollideWithBullet!!")
        return True

And in the main loop somewhere:

def run(self):
    if self.player.collideWithBullet():
         print("Game Over")

Hopefully that works for you!!!

Answered By: jaden.joeyak
The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published. Required fields are marked *