Collision between masks in pygame

I have a problem with collisions in my game, spaceship’s mask doesn’t collide properly with a background and I believe that offset is a problem, however I’m not sure.

I’ve tried multiple collision techniques and checked a lot of answers to my problem, but none of them helped me.

import pygame as pg
import os

os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (2, 29)

pg.init()


def gameLoop():
    display_width = 1800
    display_height = 1000
    pg.display.set_caption("Kerbal Space Landing Simulator")
    clock = pg.time.Clock()
    display = pg.display.set_mode((display_width, display_height))
    sprsp = pg.image.load('C:/Users/PC/PycharmProjects/untitled/sprspaceship.png').convert_alpha()
    cosbg = pg.image.load('C:/Users/PC/PycharmProjects/untitled/cosmos bg.png').convert_alpha()
    done = False

class Spaceship:
    def __init__(self, x, y, mass):
        self.x = x
        self.y = y
        self.width = 139
        self.height = 106
        self.velx = 0
        self.vely = 0
        self.mass = mass
        self.color = (255, 255, 255)
        self.spr = sprsp
        self.fuel = 500
        self.mask = pg.mask.from_surface(self.spr)
        self.angle = 0
        self.changerot = 0

    def check_controls(self):
        if keys[pg.K_SPACE] and self.fuel > 0:
            if self.angle > 0:
                self.vely += 0.005 * (self.angle - 90)
                self.velx += -0.005 * self.angle
            else:
                self.vely += -0.005 * (self.angle + 90)
                self.velx += -0.005 * self.angle

            self.fuel += -3
        if keys[pg.K_LEFT] and self.angle < 90:
            self.angle += 2
        if keys[pg.K_RIGHT] and self.angle > -90:
            self.angle += -2

    def update_pos(self):
        self.vely += 0.01
        self.x += self.velx
        self.y += self.vely
        self.mask = pg.mask.from_surface(self.spr)

    def update_rotation(self):
        self.rspr = pg.transform.rotate(self.spr, self.angle)
        self.changerot -= self.angle

    def draw(self):
        if self.fuel > 0:
            pg.draw.rect(display, (255, 255, 255), (display_width - 100, 100 + 500 - self.fuel, 10, self.fuel), 0)
        display.blit(self.rspr, (int(self.x), int(self.y)))

        self.changerot = 0

class Terrain(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.mask = pg.mask.from_threshold(display, (160, 160, 160))
        self.hitbox = pg.Rect(self.x, self.y, display_width, 500)
        self.ox = display_width // 2 - self.x // 2
        self.oy = display_height // 2 - self.y // 2

    def draw(self):
        pg.draw.rect(display, (160, 160, 160), (self.x, self.y, display_width, 500), 0)

spaceship = (Spaceship(500, 100, 1))
terrain = (Terrain(0, 800))

def redrawGameWindow():
    display.blit(cosbg, (0, 0))
    spaceship.draw()
    terrain.draw()
    pg.display.update()

def check_for_collisions():
    offset = (int(spaceship.x - terrain.ox), int(spaceship.y - terrain.oy))
    print(offset)
    print(spaceship.mask.overlap(terrain.mask, offset))
    return spaceship.mask.overlap(terrain.mask, offset)
    # return spaceship.hitbox.colliderect(terrain.hitbox)
    # return pg.sprite.spritecollide(spaceship.spr, terrain.mask, False, pg.sprite.collide_mask)

while not done:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            done = True

    keys = pg.key.get_pressed()
    mouse_pressed = pg.mouse.get_pressed()
    x, y = pg.mouse.get_pos()

    spaceship.check_controls()
    spaceship.update_pos()
    spaceship.update_rotation()
    if check_for_collisions() is not None:
        print('Hit! You've hit the ground with the speed:', spaceship.vely)
        exit()

    redrawGameWindow()
    clock.tick(60)

gameLoop()

Spaceship doesn’t collide with the surface. I know how to fix it using simpler code, but in future I want to use randomly generated terrain. Could you help me with these collisions?

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

The mask for the Terrain is never set. Crate a proper Terrain mask:

class Terrain(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
        maskSurf = pg.Surface((display_width, display_height)).convert_alpha()
        maskSurf.fill(0)
        pg.draw.rect(maskSurf, (160, 160, 160), (self.x, self.y, display_width, 500), 0)
        self.mask = pg.mask.from_surface(maskSurf)
        print(self.mask.count())
 
        # [...]

When using pygame.mask.Mask.overlap(), then you’ve to check the overlapping of the Spaceship and the Terrain, rather than the Terrain and the Spaceship.
Since the Terrain mask is a mask of the entire screen, the offset for the overlap() test is the position of the Spaceship:

def check_for_collisions():
    offset = (int(spaceship.x), int(spaceship.y))
    collide = terrain.mask.overlap(spaceship.mask, offset)
    print(offset, collide)
    return collide

Minimal example: Collision between masks in pygame repl.it/@Rabbid76/PyGame-SurfaceMaskIntersect

Collision between masks in pygame

See also: Mask


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