Search code examples
pythonpygamespritecollisionmask

Pygame collision registers hit all the time


I'm trying to change collision for my game and I saw pygame has mask collision so I tried to use it, but collision is registered all the time and I dont know how to fix it

Here's my code

Class Enemy for fishes spawning in from both sides of the screen and Respawning after hit

class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.x = 0
        self.y = 0
        self.x_change = 0

    def enemySpawn(self, i, x, x_change, y, img, screen):
        if i < 6:
            if x[i] <= -64 or x[i] >= 900:
                if i % 2 == 1:
                    x[i] = random.randint(-64, 0)
                    x_change[i] = abs(x_change[i])
                else:
                    x[i] = random.randint(834, 900)
                    x_change[i] = -x_change[i]
                y[i] = random.randint(0, 500)

        elif 5 < i < 10:
            if x[i] <= -128 or x[i] >= 964:
                if i % 2 == 1:
                    x[i] = random.randint(-128, -64)
                    x_change[i] = abs(x_change[i])
                else:
                    x[i] = random.randint(900, 964)
                    x_change[i] = -x_change[i]
                y[i] = random.randint(0, 500)

        elif i > 9:
            if x[i] <= -256 or x[i] >= 1028:
                if i % 2 == 1:
                    x[i] = random.randint(-256, -128)
                    x_change[i] = abs(x_change[i])
                else:
                    x[i] = random.randint(964, 1028)
                    x_change[i] = -x_change[i]
                y[i] = random.randint(0, 500)
        screen.blit(img[i], (x[i], y[i]))
        self.rect = img[i].get_rect()
        self.mask = pygame.mask.from_surface(img[i])

    def enemyRespawn(self, i, x, x_change, y, img, screen):
        if i < 6:
            if i % 2 == 1:
                x[i] = random.randint(-64, 0)
                x_change[i] = abs(x_change[i])
            else:
                x[i] = random.randint(834, 900)
                x_change[i] = -x_change[i]
            y[i] = random.randint(0, 500)

        elif 5 < i < 10:
            if i % 2 == 1:
                x[i] = random.randint(-128, -64)
                x_change[i] = abs(x_change[i])
            else:
                x[i] = random.randint(900, 964)
                x_change[i] = -x_change[i]
            y[i] = random.randint(0, 500)

        elif i > 9:
            if i % 2 == 1:
                x[i] = random.randint(-256, -128)
                x_change[i] = abs(x_change[i])
            else:
                x[i] = random.randint(964, 1028)
                x_change[i] = -x_change[i]
            y[i] = random.randint(0, 500)
        screen.blit(img[i], (x[i], y[i]))
        self.rect = img[i].get_rect()
        self.mask = pygame.mask.from_surface(img[i])

Class Player, that's a shark that moves by key input and get's bigger by eating

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.x = 420
        self.y = 270
        self.x_change = 0
        self.y_change = 0
        self.direction = 0
        self.lastKey = 'l'
        self.score = 0
        self.height = 128
        self.width = 128
        self.keyInput = True

    def drawImg(self, x, y, img, screen):
        screen.blit(img, (x, y))

    def setDimenzije(self, img):
        self.height = img.get_height()
        self.width = img.get_width()

    def playerMovement(self, img, screen):
        # Provera u koju stranu ide
        pressed = pygame.key.get_pressed()
        if self.keyInput:
            if pressed[pygame.K_LEFT]:
                self.x_change = -1.5
                self.direction = -1
            if pressed[pygame.K_RIGHT]:
                self.x_change = +1.5
                self.direction = 1
            if pressed[pygame.K_LEFT] and pressed[pygame.K_RIGHT]:
                self.x_change = 0
            if not (pressed[pygame.K_LEFT] or pressed[pygame.K_RIGHT]):
                self.x_change = 0
            if pressed[pygame.K_UP]:
                self.y_change = -1.5
            if pressed[pygame.K_DOWN]:
                self.y_change = +1.5
            if pressed[pygame.K_UP] and pressed[pygame.K_DOWN]:
                self.y_change = 0
            if not (pressed[pygame.K_UP] or pressed[pygame.K_DOWN]):
                self.y_change = 0

        self.x += self.x_change
        self.y += self.y_change

        if self.direction == 1 or self.lastKey == 'r':
            img_flip = pygame.transform.flip(img, True, False)
            if 5 < self.score < 50:
                self.setDimenzije(img_flip)
                self.drawImg(self.x, self.y, img_flip, screen)
            else:
                img = pygame.transform.scale(img_flip, (64, 64))
                self.setDimenzije(img)
                self.drawImg(self.x, self.y, img, screen)
        elif self.lastKey == 'l':
            if 5 < self.score < 50:
                self.setDimenzije(img)
                self.drawImg(self.x, self.y, img, screen)
            else:
                img = pygame.transform.scale(img, (64, 64))
                self.setDimenzije(img)
                self.drawImg(self.x, self.y, img, screen)

        if self.x <= -60:
            self.x = 900
        elif self.x >= 900:
            self.x = -60
        if self.y >= 534:
            self.y = 534
        if self.y <= 0:
            self.y = 0
        self.rect = img.get_rect()
        self.mask = pygame.mask.from_surface(img)

Main Class that has my Collision

enemyImg = []
        
 player = Player()
 enemy = Enemy()
 enemySprite = pygame.sprite.Group()

 def main():
    # Inicijalizacija
    pygame.init()
    clock = pygame.time.Clock()

    num_of_enemies = 12

    font = pygame.font.Font('freesansbold.ttf', 32)
    over_font = pygame.font.Font('freesansbold.ttf', 48)

    # kreiram ekran
    screen = pygame.display.set_mode((900, 600))

    # Pozadina
    pozadina = pygame.image.load("pozadina.png")

    # Naslov i ikona
    pygame.display.set_caption("Gladna ajkula")
    icon = pygame.image.load("sharkIcon.png")
    pygame.display.set_icon(icon)

    enemyImg0 = pygame.image.load("fish1smol-left.png").convert_alpha()
    enemyImg1 = pygame.image.load("fish1smol-right.png").convert_alpha()

    enemyImg2 = pygame.image.load("fish2smol-left.png").convert_alpha()
    enemyImg3 = pygame.image.load("fish2smol-right.png").convert_alpha()

    enemyImg4 = pygame.image.load("fish3smol-left.png").convert_alpha()
    enemyImg5 = pygame.image.load("fish3smol-right.png").convert_alpha()

    enemyImg6 = pygame.image.load("fish1med-left.png").convert_alpha()
    enemyImg7 = pygame.image.load("fish1med-right.png").convert_alpha()

    enemyImg8 = pygame.image.load("fish3med-left.png").convert_alpha()
    enemyImg9 = pygame.image.load("fish3med-right.png").convert_alpha()

    enemyImg10 = pygame.image.load("orca-left.png").convert_alpha()
    enemyImg11 = pygame.image.load("orca-right.png").convert_alpha()

    enemyImg = [enemyImg0, enemyImg1, enemyImg2, enemyImg3, enemyImg4, enemyImg5, enemyImg6, enemyImg7, enemyImg8,
            enemyImg9, enemyImg10, enemyImg11]

    # Igrac
    playerImgLevo1 = pygame.image.load("SharkScaleLeft.png").convert_alpha()
    playerImgDesno1 = pygame.image.load("SharkScaleRight.png").convert_alpha()

    player.keyInput = True

    enemyList = []
    enemyX = []
    enemyY = []
    enemyX_change = []

    for i in range(num_of_enemies):
        if i % 2 == 1:
            enemyX.append(random.randint(-64, 0))
            enemyX_change.append(1)
        else:
            enemyX.append(random.randint(850, 900))
            enemyX_change.append(-1)
        enemyY.append(random.randint(0, 500))
        enemyList.append(enemy)

    running = True
    while running:
        clock.tick(120)
        # Pozadina
        screen.blit(pozadina, (0, 0))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        player.playerMovement(playerImgLevo1, screen)

        # Enemy
        for i in range(12):
            enemyX[i] += enemyX_change[i]
            enemy.enemySpawn(i, enemyX, enemyX_change, enemyY, enemyImg, screen)
            enemySprite.add(enemy)

           mob_hits = pygame.sprite.spritecollide(player, enemySprite, False, pygame.sprite.collide_mask)
            #mob_hits = pygame.sprite.collide_mask(player, enemyList[i])
            if mob_hits:
                 if player.width > enemyImg[i].get_width():
                 enemy.enemyRespawn(i, enemyX, enemyX_change, enemyY, enemyImg, screen)
                 player.score += 1
                 print('hit')

I know it's kinda messy at this point, but i tried to get hit with just collide mask, and spritecollide but i don't know where did I go wrong. Enemies just keep respawning countless times and pump my score to 1000 in a sec


Solution

  • For the collision detection functions like pygame.sprite.spritecollide the rect attribute of the pygame.sprtie.Sprite objects is used. Therefore you need to make sure that the position of the sprite is set in the rect attribute of the Enemy

    self.rect = img[i].get_rect()

    self.rect = img[i].get_rect(topleft = (x[i], y[i]))
    

    and the Player

    self.rect = img.get_rect()

    self.rect = img.get_rect(topleft = (self.x, self.y))
    

    Note, pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. A Surface is blit at a position on the screen. The position of the rectangle can be specified by a keyword argument. For example, the top left of the rectangle can be specified with the keyword argument topleft. These keyword argument are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a full list of the keyword arguments).