pythonpygamecollision-detection

Trying to spawn n non-colliding sprites; pygame crashing


I'm very new to PyGame (started learning two days ago) and I'm trying to make a game in which a random amount of berries grow on my island.

My (ideal) algorithm would

  1. Spawn in a static berry bush
  2. Check if it is colliding with any berries I spawned previously
  3. If it is colliding, delete it and spawn another which isn't colliding

I've written the below code for the genBerries function:

def genBerries(xRange, yRange, bushCount):  
    sucSpawns = 0
    berries = []
    while sucSpawns < bushCount:  # while you've spawned less than you want to
        spawnX = random.randint(xRange[0], xRange[1])  # randomise x coordinate for berry bush
        spawnY = random.randint(yRange[0], yRange[1])  # randomise y coordinate for berry bush
        berry = Berry(10, 5, 3, spawnX, spawnY)  # make a berry at this x and y
        for j in berries:
            collide = berry.rect.colliderect(j)  # check if berry colliding with some other berry
            if collide:
                berry.kill()  # if it is, get rid of it (and while loop will let u try again)
            else:
                sucSpawns += 1
                berries.append(berry)  # add this berry to the list of berries you've spawned successfully

xRange = island.rect.left, island.rect.right - 96  # defining x range berries can spawn in
yRange = (island.rect.top + 96, island.rect.bottom)  # defining y range berries can spawn in
bushCount = math.floor(random.normalvariate(conf['bA'], conf['bA']/5))  # defining how many berries spawn

genBerries(xRange, yRange, bushCount)

However, as soon as I do this...

not responding

Some kind of stack overflow (ha ha) seems to mean this process isn't terminating?? And then black-screens??

If I comment out the collision part, it gives me (as desired, but overlapping): overlapping berries


Solution

  • You have to leave the loop when you recognize a collision and may only insert the object once into the list, but not for each object with which it does not collide:

    while len(berries) < bushCount:
        spawnX = random.randint(xRange[0], xRange[1]) 
        spawnY = random.randint(yRange[0], yRange[1])
        berry = Berry(10, 5, 3, spawnX, spawnY)
        collide = False 
        for testBerry in berries:
            collide = berry.rect.colliderect(testBerry.rect)
            if collide:
                berry.kill()
                break
        if collide == False:
            berries.append(berry)
    

    You can use collidelist() to simplify the code:

    while len(berries) < bushCount:
        spawnX = random.randint(xRange[0], xRange[1]) 
        spawnY = random.randint(yRange[0], yRange[1])
        berry = Berry(10, 5, 3, spawnX, spawnY)
        collideIndex = berry.rect.collidelist(berries)
        if collideIndex == -1:
            berries.append(berry)
        else: 
            berry.kill()
    

    Minimal example:

    import pygame
    import random
    
    pygame.init()
    window = pygame.display.set_mode((400, 400))
    clock = pygame.time.Clock()
    
    bushCount = 4
    berries = []
    xRange = [100, 300]
    yRange = [100, 300]
    
    berrGroup = pygame.sprite.Group()
    class Berry(pygame.sprite.Sprite):
        def __init__(self, x, y):
            super().__init__()
            self.image = pygame.Surface((40, 40))
            self.image.fill((255, 0, 0))
            self.rect = self.image.get_rect(center = (x, y))
            berrGroup.add(self)
    
    run = True
    while run:
        clock.tick(5)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False 
        
        berrGroup.empty()
        berries.clear()
        while len(berries) < bushCount:
            spawnX = random.randint(xRange[0], xRange[1]) 
            spawnY = random.randint(yRange[0], yRange[1])
            berry = Berry(spawnX, spawnY)
            collideIndex = berry.rect.collidelist(berries)
            if collideIndex == -1:
                berries.append(berry) 
            else: 
                berry.kill()
    
        window.fill(0)
        berrGroup.draw(window)
        pygame.display.flip()
    
    pygame.quit()
    exit()