Python/Pygame can't get collisions to detect correctly

Question:

The collisions between the creature class objects and plant class objects in my code will not register, if anyone has any ideas on how I can get each rectangle to check for collisions with a sprite group, delete that member of the sprite group it’s colliding into, and effect a statistic in only the member of the creature class that collided with the secondary sprite group. Please help me out if you can, and thank you for your time.

class Creature(pg.sprite.Sprite):

    def __init__(self, pos, game_area):
        super().__init__()
        self.image = pg.Surface((5, 5))
        self.image.fill(pg.Color('Blue'))
        self.rect = self.image.get_rect(center=pos)
        self.vel = Vector2(2, 0).rotate(random.randrange(360))
        self.pos = Vector2(pos)
        self.game_area = game_area
        self.energymax=10000
        self.energy=10000

    def update(self):
        self.pos += self.vel
        self.rect.center = self.pos
        self.vel = Vector2(2, 0).rotate(random.randrange(360))
        if not self.game_area.contains(self.rect):
            self.kill()
        self.energy=self.energy-10
        if self.energy<=0:
            self.kill()
        pg.sprite.spritecollide(self,plant_sprites,True)
        if pg.sprite.spritecollide(self,plant_sprites,True) and self.energy<self.energymax-1000:
            self.energy=self.energy+1000
            Plant.self.kill()


            
def main():
    screen = pg.display.set_mode((640, 480))
    game_area = pg.Rect(60, 60, 520, 360)
    game_area_color = pg.Color('aquamarine2')
    clock = pg.time.Clock()
    all_sprites = pg.sprite.Group(Creature(game_area.center, game_area))
    plant_sprites=pg.sprite.Group(Plant(game_area.center, game_area))
    q_pressed = False
    p_pressed = False

    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_q:
                    q_pressed = True
                if event.key == pg.K_p:
                    p_pressed = True
            if event.type == pg.KEYUP:
                if event.key == pg.K_q:
                    q_pressed = False
                if event.key == pg.K_p:
                    p_pressed = False
                
        mp=pg.mouse.get_pos()
        if mp!=None and q_pressed==True:
            all_sprites.add(Creature(mp, game_area))
        all_sprites.update()
        if mp!=None and p_pressed==True:
            all_sprites.add(Plant(mp, game_area))

        screen.fill((30, 30, 30))
        all_sprites.draw(screen)
        pg.draw.rect(screen, game_area_color, game_area, 2)

        pg.display.flip()
        clock.tick(60)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()

Final code after problem was solved:


import pygame as pg
from pygame.math import Vector2

class Plant(pg.sprite.Sprite):

    def __init__(self, pos, game_area):
        super().__init__()
        self.image = pg.Surface((2, 2))
        self.image.fill(pg.Color('Green'))
        self.rect = self.image.get_rect(center=pos)
        self.game_area = game_area

    def update(self):
        if not self.game_area.contains(self.rect):
            self.kill()

screen = pg.display.set_mode((640, 480))
game_area = pg.Rect(60, 60, 520, 360)
game_area_color = pg.Color('aquamarine2')
clock = pg.time.Clock()
plant_sprites=pg.sprite.Group(Plant(game_area.center, game_area))
q_pressed = False
p_pressed = False

class Creature(pg.sprite.Sprite):

    def __init__(self, pos, game_area):
        super().__init__()
        self.image = pg.Surface((5, 5))
        self.image.fill(pg.Color('Blue'))
        self.rect = self.image.get_rect(center=pos)
        self.vel = Vector2(2, 0).rotate(random.randrange(360))
        self.pos = Vector2(pos)
        self.game_area = game_area
        self.energymax=10000
        self.energy=10000

    def update(self):
        self.pos += self.vel
        self.rect.center = self.pos
        self.vel = Vector2(2, 0).rotate(random.randrange(360))
        if not self.game_area.contains(self.rect):
            self.kill()
        self.energy=self.energy-10
        if self.energy<=0:
            self.kill()
        if pg.sprite.spritecollide(self,plant_sprites,True):
            if self.energy<self.energymax-2000:
                self.energy=self.energy+2000


            
def main():
    screen = pg.display.set_mode((640, 480))
    game_area = pg.Rect(60, 60, 520, 360)
    game_area_color = pg.Color('aquamarine2')
    clock = pg.time.Clock()
    global plant_sprites
    all_sprites = pg.sprite.Group(Creature(game_area.center, game_area))
    plant_sprites=pg.sprite.Group(Plant(game_area.center, game_area))
    q_pressed = False
    p_pressed = False

    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_q:
                    q_pressed = True
                if event.key == pg.K_p:
                    p_pressed = True
            if event.type == pg.KEYUP:
                if event.key == pg.K_q:
                    q_pressed = False
                if event.key == pg.K_p:
                    p_pressed = False
                
        mp=pg.mouse.get_pos()
        if mp!=None and q_pressed==True:
            all_sprites.add(Creature(mp, game_area))
        all_sprites.update()
        if mp!=None and p_pressed==True:
            global new_plant
            new_plant = Plant(mp, game_area)
            plant_sprites.add(new_plant)

        screen.fill((30, 30, 30))
        all_sprites.draw(screen)
        plant_sprites.draw(screen)
        pg.draw.rect(screen, game_area_color, game_area, 2)

        pg.display.flip()
        clock.tick(60)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()```
Asked By: Hello

||

Answers:

See pygame.sprite.spritecollide

Return a list containing all Sprites in a Group that intersect with another Sprite. Intersection is determined by comparing the Sprite.rect attribute of each Sprite.

The dokill argument is a bool. If set to True, all Sprites that collide will be removed from the Group.

Therefore, if you call this function twice in one line with dokill set True, the 2nd call does not detect collisions, as all colliding objects have been removed before.

Call pygame.sprite.spritecollide just once:

class Creature(pg.sprite.Sprite):

    # [...]

    def update(self):
        self.pos += self.vel
        self.rect.center = self.pos
        self.vel = Vector2(2, 0).rotate(random.randrange(360))
        if not self.game_area.contains(self.rect):
            self.kill()
        self.energy=self.energy-10
        if self.energy<=0:
            self.kill()
        
        # pg.sprite.spritecollide(self,plant_sprites,True) <--- DELETE
        
        if pg.sprite.spritecollide(self,plant_sprites,True) and self.energy<self.energymax-1000:
            self.energy=self.energy+1000
            Plant.self.kill()

You must use the global statement, if you want to set a variable in global namespace within a function:

def main():
    global plant_sprites

    # [...]

    plant_sprites = pg.sprite.Group(Plant(game_area.center, game_area))

    # [...]

You must add new Plants to the plant_sprites Group:

if mp!=None and p_pressed==True:
    new_plant = Plant(mp, game_area)
    plant_sprites.add(new_plant)
Answered By: Rabbid76
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.