Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pixel perfect collision detection for sprites with a transparent background

How can I use Pygame's sprite module to detect the collision of two sprites with transparent backgrounds, so that it will return True only if the actual sprites, not the transparent backgrounds collide?

like image 386
Peter Y. Avatar asked Mar 07 '23 06:03

Peter Y.


1 Answers

Use the pygame.mask.from_surface function to give your sprites a self.mask attribute.

self.mask = pygame.mask.from_surface(self.image)

Then you can pass pygame.sprite.collide_mask as the callback function to one of the sprite collision functions like pygame.sprite.spritecollide and the collision detection will be pixel perfect.

pygame.sprite.spritecollide(self.player, self.enemies, False, pygame.sprite.collide_mask)

Here's a complete example (the caption is changed when the two sprites collide):

import pygame as pg


class Player(pg.sprite.Sprite):

    def __init__(self, pos):
        super(Player, self).__init__()
        self.image = pg.Surface((120, 120), pg.SRCALPHA)
        pg.draw.polygon(self.image, (0, 100, 240), [(60, 0), (120, 120), (0, 120)])
        self.rect = self.image.get_rect(center=pos)
        self.mask = pg.mask.from_surface(self.image)


class Enemy(pg.sprite.Sprite):

    def __init__(self, pos):
        super(Enemy, self).__init__()
        self.image = pg.Surface((120, 120), pg.SRCALPHA)
        pg.draw.circle(self.image, (240, 100, 0), (60, 60), 60)
        self.rect = self.image.get_rect(center=pos)
        self.mask = pg.mask.from_surface(self.image)


class Game:
    def __init__(self):
        self.screen = pg.display.set_mode((640, 480))
        self.player = Player((20, 20))
        self.enemies = pg.sprite.Group(Enemy((320, 240)))
        self.all_sprites = pg.sprite.Group(self.player, self.enemies)
        self.done = False
        self.clock = pg.time.Clock()

    def run(self):
        while not self.done:
            self.event_loop()
            self.update()
            self.draw()
            pg.display.flip()
            self.clock.tick(60)

    def event_loop(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            elif event.type == pg.MOUSEMOTION:
                self.player.rect.center = event.pos

    def update(self):
        # Check if the player collides with an enemy sprite. The
        # `pygame.sprite.collide_mask` callback uses the `mask`
        # attributes of the sprites for the collision detection.
        if pg.sprite.spritecollide(self.player, self.enemies, False, pg.sprite.collide_mask):
            pg.display.set_caption('collision')
        else:
            pg.display.set_caption('no collision')

    def draw(self):
        self.screen.fill((30, 30, 30))
        self.all_sprites.draw(self.screen)


if __name__ == '__main__':
    pg.init()
    game = Game()
    game.run()
    pg.quit()
like image 74
skrx Avatar answered Mar 09 '23 19:03

skrx