2

I have made a putt-putt game and now I want to add a slanted wall type. Because of this, I need to use masks for the collision (until now I have just used rects). I have spent hours learning about masks and trying to figure out why my code won't work. There are no errors, the collision just isn't detected.

I have simplified my code down to something much smaller just as a way for me to test it efficiently. From everything I've seen this seems like it should work, but it doesnt. Here it is:

import pygame

# Pygame init stuff
pygame.init()

wind_width = 1200
wind_height = 700

gameDisplay = pygame.display.set_mode((wind_width, wind_height))
pygame.display.set_caption("Mini Golf!")

pygame.display.update()

gameExit = False

clock = pygame.time.Clock()

# Class setups
class Ball:

    def __init__(self, x, y):
        self.x = x
        self.y = y

        self.image = pygame.image.load("sball.png")
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)

    def render(self):
        self.rect.topleft = (self.x, self.y)
        gameDisplay.blit(self.image, self.rect)

class Slant:

    def __init__(self, x, y):
        self.x = x
        self.y = y

        self.image = pygame.image.load("posslant.png")
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)

    def render(self):
        self.rect.topleft = (self.x, self.y)
        gameDisplay.blit(self.image, self.rect)

# Creating objects
ball = Ball(250, 250)

slant = Slant(270, 250)

# Game loop
gameExit = False
while not(gameExit):

    # Moves ball
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            gameExit = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                ball.y -= 1
            elif event.key == pygame.K_DOWN:
                ball.y += 1
            elif event.key == pygame.K_LEFT:
                ball.x -= 1
            elif event.key == pygame.K_RIGHT:
                ball.x += 1

    # Collision detection
    offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
    if slant.mask.overlap(ball.mask, (offset_x, offset_y)):
        print("hit")

    # Draws everything
    gameDisplay.fill((0, 0, 0))
    ball.render()
    slant.render()

    pygame.display.update()

    clock.tick(100)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Logan Holmes
  • 89
  • 1
  • 1
  • 9
  • Please provide a minimum reproducible example. – Axiumin_ Aug 12 '19 at 04:55
  • Do you use transparent pixels in your images? I guess you should call `convert_alpha()` on your surfaces (e.g.: `self.image = pygame.image.load("sball.png").convert_alpha()`), but I can't say for sure without having your images. – sloth Aug 12 '19 at 07:12
  • My images are transparent so I didnt think I would need to do any converting – Logan Holmes Aug 12 '19 at 12:47
  • I just tried using ```covert_alpha()``` and it did not seem to change anything – Logan Holmes Aug 12 '19 at 17:11
  • Maybe have a look here? https://stackoverflow.com/questions/46862739/sprite-mask-collision-problems-in-pygame – ChatterOne Aug 14 '19 at 12:05

1 Answers1

4

The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.
So the offset is calculated by subtracting the coordinates of slant from the coordinates of ball:

offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)

offset = (ball.rect.x - slant.rect.x), (ball.rect.y - slant.rect.y)
if slant.mask.overlap(ball.mask, offset):
    print("hit")
    

When you create the mask images, then I recommend to ensure that the image has per pixel alpha format by calling .convert_alpha():

class Ball:

    def __init__(self, x, y):
        self.x = x
        self.y = y

        self.image = pygame.image.load("sball.png")
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image.convert_alpha()) # <---
class Slant:

    def __init__(self, x, y):
        self.x = x
        self.y = y

        self.image = pygame.image.load("posslant.png")
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image.image.convert_alpha()) # <---

Minimal example: repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect

See also: Mask

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • That makes sense, but still does not fix the code. I still never get the print statement. Thanks for the help nonetheless. – Logan Holmes Aug 15 '19 at 04:15
  • Strange. Could it have something to do with my images than? They are both .png. I have uploaded them here: https://imgur.com/a/byjGC8M – Logan Holmes Aug 16 '19 at 15:52
  • @LoganHolmes The bit depth of the white rectangle was 24. I restored it with a depth of 32 and it works: https://imgur.com/NwRVnad. But you can fix this by calling `.convert_alpha()`. See the answer. – Rabbid76 Aug 16 '19 at 16:42
  • Thank you so much it works now. I have been stuck on this for so long. I can't thank you enough for taking all the time to help me. – Logan Holmes Aug 18 '19 at 01:10