Search code examples
pythonpygamespritecollision-detectionpygame-surface

Can't figure out how to check mask collision between two sprites


I have two different sprites in the same group, variables 'player' and 'ground'. They both are separate classes, with a mask of their surface. This line is in both of their classes.

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

The images used in their surfaces have 'convert_alpha()' used on them so part of them is transparent, and the mask should work on them. The ground is a few platforms, and I want to check for collision so I can keep the player on the ground, and have them fall when they are not on the non-transparent parts.

if pygame.sprite.collide_mask(player,ground):
        print("collision")
else:
        print("nope")

This prints "nope", even as the player sprite is falling over where the colored ground sprite pixels are. So the documentation for 'collide_mask()' says that it returns "NoneType" when there is no collision. So I tried this.

if pygame.sprite.collide_mask(player,ground)!= NoneType:
        print("collision")

This prints "collision" no matter where the player is(I have jumping, left, and right movements setup for the player). I asked a question about collision yesterday with no answer that helped. And I was told to condense my code submitted in the question so hopefully I explained this well enough without posting all 90 lines. I've checked a lot of other questions on here, and they all seem to do it a little different so I'm very confused (and fairly new). Emphasis on both sprites being in the same group, I couldn't get spritecollide() to work because of this.


Solution

  • The sprites do not only need the mask attribute, they also need the rect attribute. the mask defines the bitmask and rect specifies the posiotion of the sprite on the screen. See pygame.sprite.collide_mask:

    Tests for collision between two sprites, by testing if their bitmasks overla. If the sprites have a mask attribute, it is used as the mask, otherwise a mask is created from the sprite's image. Sprites must have a rect attribute; the mask attribute is optional.

    If sprites are used in pygame.sprite.Groups then each sprite should have image and rect attributes. pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.

    The latter delegates to the update method of the contained pygame.sprite.Sprites — you have to implement the method. See pygame.sprite.Group.update():

    Calls the update() method on all Sprites in the Group. [...]

    The former uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects — you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():

    Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]


    Minimal example

    import os, pygame
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    
    class SpriteObject(pygame.sprite.Sprite):
        def __init__(self, x, y, image):
            super().__init__()
            self.image = image
            self.rect = self.image.get_rect(center = (x, y))
            self.mask = pygame.mask.from_surface(self.image)
    
    pygame.init()
    clock = pygame.time.Clock()
    window = pygame.display.set_mode((400, 400))
    size = window.get_size()
    
    object_surf = pygame.image.load('AirPlane.png').convert_alpha()
    obstacle_surf = pygame.image.load('Rocket').convert_alpha()
    
    moving_object = SpriteObject(0, 0, object_surf)
    obstacle = SpriteObject(size[0] // 2, size[1] // 2, obstacle_surf)
    all_sprites = pygame.sprite.Group([moving_object, obstacle])
    
    run = True
    while run:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        moving_object.rect.center = pygame.mouse.get_pos()
        collide = pygame.sprite.collide_mask(moving_object, obstacle)
        
        window.fill((255, 0, 0) if collide else (0, 0, 64))
        all_sprites.draw(window)
        pygame.display.update()
    
    pygame.quit()
    exit()