2
votes

Hi I recently started to get into the pygame library and making some small projects. I'm making just a very simple game in which you as a player have to dodge the enemies. However, I want to delete the mobs when they collide with each other. I thought of the following code, however, it only removes one of the mobs. The mobs are in both all_sprites, and in mob_sprites. I'm very new on sprites and pygame, so there probably is a silly mistake inhere, hopefully someone can help me.

# check mob collision
for mob in mobs:
    temp_sprites.add(mob)
    mobs.remove(mob)
    collision = pg.sprite.groupcollide(temp_sprites, mobs, True, True)
    for col in collision:
        # score is just for the game
        score += col.size
    else:
        mobs.add(mob)
        all_sprites.add(mob)

    temp_sprites.remove(mob)
2

2 Answers

1
votes

Your first problem is that you use a for...else loop; and the else part will be executed if you don't break the for loop in case of a collision, and thus re-adding the sprite.

The second problem with your code is that while groupcollide will correctly remove the sprites from their groups, they will be readded since they are still stored in the list you're iterating over with your for loop (iterating over a sprite group will create a new list every time).

So you can either fix your code with something like this:

    for mob in mobs.sprites():
        
        if not mob.groups():
            # mob was already removed by a previous iteration of this loop
            continue

        temp_sprites.add(mob)
        mobs.remove(mob)
        collision = pygame.sprite.groupcollide(temp_sprites, mobs, True, True)
        for col in collision:
            # score is just for the game
            score += col.size
            break
        else:
            mobs.add(mob)
            all_sprites.add(mob)

        temp_sprites.remove(mob)

but I would suggest to handle the collision in the update method of the sprite instead.

def update(self):
    # whatever
    if pygame.sprite.spritecollide(self, self.mobs, True, collide_rect_not_self):
        self.kill()

where self.mobs is a reference to the mobs group and collide_rect_not_self is a simple wrapper around pygame.sprite.collide_rect:

def collide_rect_not_self(a, b):
    if a != b:
        return pygame.sprite.collide_rect(a, b)

Here's a full example:

import random
import pygame

def collide_rect_not_self(a, b):
    if a != b:
        return pygame.sprite.collide_rect(a, b)

class Actor(pygame.sprite.Sprite):
    def __init__(self, pos, mobs, static, *grps):
        super().__init__(mobs, *grps)
        self.image = pygame.Surface((40, 40))
        self.rect = self.image.get_rect(center=pos)
        self.pos = pygame.Vector2(*pos)
        self.vel = pygame.Vector2(random.randint(0, 10), random.randint(0, 10)) if not static else pygame.Vector2(0, 0)
        self.mobs = mobs

    def update(self):
        self.pos += self.vel
        if not pygame.display.get_surface().get_rect().contains(self.rect):
            self.vel *= -1
            self.rect.clamp_ip(pygame.display.get_surface().get_rect())
            self.pos = self.rect.center
        self.rect.center = self.pos
        if pygame.sprite.spritecollide(self, self.mobs, True, collide_rect_not_self):
            self.kill()

def main():
    pygame.init()
    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((800, 600))
    mobs = pygame.sprite.Group()
    all_sprites = pygame.sprite.Group()
    while True:

        for event in pygame.event.get():
            pos = pygame.mouse.get_pos()
            if event.type == pygame.QUIT:
                return

            if event.type == pygame.MOUSEBUTTONDOWN:
                Actor(event.pos, mobs, event.button == 1, all_sprites)
                    
        screen.fill((255, 255, 255))
        all_sprites.update()
        all_sprites.draw(screen)
        clock.tick(30)
        
               
        pygame.display.flip()
main()

Use the left mouse button to place a static rect and the other mouse buttons to place a moving one.

enter image description here

1
votes

If you want to destroy a sprite, then it is sufficient to invoke pygame.sprite.Sprite.kill():

mob.kill()

kill removes the Sprite from all the Groups that contain it.

If you want to detect if a sprite collides with any other sprite in a loop, then I recommend to use 2 nested loops and pygame.sprite.collide_rect():

For instance:

for mob1 in mobs:
    for mob2 in mobs:
        
        if mob1 != mob2 and pg.sprite.collide_rect(mob1, mob2):
            mob1.kill()
            mob2.kill()

            # score is just for the game
            score += col.size