Pygame mask collision

I’m trying to get proper collision detection with rotating surfaces in pygame. I decided to try using masks. It somewhat works, but it is not as precise as I’d liked/thought. I tried updating the mask at the end of the cycle to get a “fresh” hitbox for the next frame, but it didn’t work as expected. What is my mistake?

import pygame
import random

WHITE = [255, 255, 255]
RED = [255, 0, 0]

pygame.init()

FPS = pygame.time.Clock()
fps = 6

winW = 1000
winH = 500
BGCOLOR = WHITE
win = pygame.display.set_mode((winW, winH))
win.fill(WHITE)
pygame.display.set_caption('')
pygame.display.set_icon(win)


class Box(pygame.sprite.Sprite):
    def __init__(self, x, y, w, h):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([w, h], pygame.SRCALPHA)
        self.image.fill(random_color())
        self.mask = pygame.mask.from_surface(self.image)
        self.rect = pygame.Rect(x, y, w, h)
        self.angle = 0

    def move(self):
        self.rect.center = pygame.mouse.get_pos()

    def draw(self):
        blits = self.rotate()
        win.blit(blits[0], blits[1])
        self.mask = pygame.mask.from_surface(blits[0])

    def rotate(self):
        self.angle += 3
        new_img = pygame.transform.rotate(self.image, self.angle)
        new_rect = new_img.get_rect(center=self.rect.center)
        return new_img, new_rect


def update_display():
    win.fill(BGCOLOR)
    player.draw()
    for p in platforms:
        p.draw()
    pygame.display.update()


def collision():
    return pygame.sprite.spritecollide(player, plat_collide, False, pygame.sprite.collide_mask)


def random_color():
    return [random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]


player = Box(100, 400, 50, 50)

platforms = [Box(300, 400, 100, 50)]
plat_collide = pygame.sprite.Group(platforms)

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    hits = collision()
    if hits:
        BGCOLOR = RED
    else:
        BGCOLOR = WHITE

    player.move()

    update_display()

    FPS.tick(fps)

pygame.quit()

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Your application works fine. But note, pygame.sprite.collide_mask() use the .rect and .mask attribute of the sprite object for the collision detection.
You have to update self.rect after rotating the image:

class Box(pygame.sprite.Sprite):
    # [...]

    def rotate(self):
        self.angle += 3
        new_img = pygame.transform.rotate(self.image, self.angle)
        new_rect = new_img.get_rect(center=self.rect.center)
        
        # update .rect attribute
        self.rect = new_rect # <------
        
        return new_img, new_rect

See also Sprite mask


Minimal example: Pygame mask collision repl.it/@Rabbid76/PyGame-SpriteMask

Pygame mask collision

import pygame

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, w, h, color):
        pygame.sprite.Sprite.__init__(self)
        self.angle = 0
        self.original_image = pygame.Surface([w, h], pygame.SRCALPHA)
        self.original_image.fill(color)
        self.image = self.original_image
        self.rect = self.image.get_rect(center = (x, y))
        self.mask = pygame.mask.from_surface(self.image )
    def update(self):
        self.rotate()
    def rotate(self):
        self.angle += 0.3
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center = self.rect.center)
        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()

moving_object = SpriteObject(0, 0, 50, 50, (128, 0, 255))
static_objects = [
    SpriteObject(size[0] // 2, size[1] // 3, 100, 50, (128, 128, 128)),
    SpriteObject(size[0] // 4, size[1] * 2 // 3, 100, 50, (128, 128, 128)),
    SpriteObject(size[0] * 3 // 4, size[1] * 2 // 3, 100, 50, (128, 128, 128))
]
all_sprites = pygame.sprite.Group([moving_object] + static_objects)
static_sprites = pygame.sprite.Group(static_objects)

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()
    all_sprites.update() 
    collide = pygame.sprite.spritecollide(moving_object, static_sprites, False, pygame.sprite.collide_mask)
    
    window.fill((255, 0, 0) if collide else (255, 255, 255))
    all_sprites.draw(window)
    pygame.display.update()

pygame.quit()
exit()


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x