-2

Currently I'm removing bullets when either:

  • bullet collides with player
  • bullet goes out of map
  • bullet hits tile in map

for instance:

example

I remove the bullet on any of those scenarios, which most often works, however randomly the script pulls up the error above:

ValueError: list.remove(x): x not in list

This happens even after the script working previously

Current script:


class Range_Enemy(Enemy):
def __init__(self,x,y,health,x_speed,image):
    super().__init__(x,y,health,x_speed,image)
    # extra variables
    self.shoot_last = 0
    self.bullet_speed = 5
    self.bullet_y_vel = -1
    self.bullet_grav = 0.05
    self.bullets_right = []
    self.bullets_left = []
    self.bullet_colour = (0,255,0)

def personal_update(self,world):
    # checks enemy pos and changes depending on distance from player
    if self.rect.x > player.rect.x + 300:
        self.x_speed = -2
    elif self.rect.x < player.rect.x -300:
        self.x_speed = 2
    elif self.rect.x < player.rect.x + 300:
        self.x_speed = 0
    elif self.rect.x > player.rect.x - 300:
        self.x_speed = 0
    # shoots every scheduled tick
    shoot_now = p.time.get_ticks()
    if shoot_now - self.shoot_last > 1500:
        self.bullet_y_vel = -1.5
        right_bullet = p.Rect(self.rect.x,self.rect.y + 5,8,4)
        left_bullet = p.Rect(self.rect.x + 20,self.rect.y + 5,8,4)
        self.bullets_right.append(right_bullet)
        self.bullets_left.append(left_bullet)
        self.shoot_last = shoot_now
    # gets every bullet
    for bullet in self.bullets_right:
        # checks any collision
        if bullet.colliderect(player):
            player.health -= 1
            self.bullets_right.remove(bullet)
        for tile in world.tile_list:
            if tile[1].colliderect(bullet):
                self.bullets_right.remove(bullet)
        if bullet.x > WIDTH:
            self.bullets_right.remove(bullet)
        # applies movement to bullet
        bullet.x += self.bullet_speed
        # applies gravity to bullet
        self.bullet_y_vel += self.bullet_grav
        bullet.y += self.bullet_y_vel
        p.draw.rect(WIN,self.bullet_colour,bullet)

    for bullet in self.bullets_left:
        # checks for any collision
        if bullet.x < 0:
            self.bullets_left.remove(bullet)
        if bullet.colliderect(player):
            player.health -= 1
            self.bullets_left.remove(bullet)
        for tile in world.tile_list:
            if tile[1].colliderect(bullet):
                self.bullets_left.remove(bullet)
        # applies movement to bullet
        bullet.x -= self.bullet_speed
        # applies gravity to bullet
        self.bullet_y_vel += self.bullet_grav
        bullet.y += self.bullet_y_vel
        p.draw.rect(WIN,self.bullet_colour,bullet)

This creates an enemy which shoots bullets both right and left in a small curve.

I don't understand why this error comes up as it only removes the bullet from the list when one of the collisions is met, and this usually works. It is only randomly this error occurs however ruins the whole dynamic of the game.

The error suggests that the script is removing a bullet which doesn't exist, so is there a way to check if the bullet doesn't exist then just passes, or is there a way to simply stop this from happening?

Jakob Lovern
  • 1,301
  • 7
  • 24
B1ake
  • 11
  • 5
  • 1
    Please supply the expected [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) (MRE). We should be able to copy and paste a contiguous block of your code, execute that file, and reproduce your problem along with tracing output for the problem points. This lets us test our suggestions against your test data and desired output. – Prune Aug 05 '21 at 22:53
  • 1
    [You shouldn't remove elements from a list while iterating through it](https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating) –  Aug 05 '21 at 22:54
  • Also your loop can execute `remove(bullet)` for the same bullet multiple times in the same iteration. That's why you get the error you're getting. – khelwood Aug 05 '21 at 22:59

1 Answers1

0

Two things: First, you should put bullets you remove into another temporary list so that you don't skip over bullets while iterating. Second, you should continue after removing a bullet / marking a bullet for removal, as otherwise, multiple conditions might be true, yet the bullet doesn't exist when you try to remove it.

Here's the for loop for the right bullets with the fixes above: (you can adapt this for the left bullets)

# Temporary list of bullets to remove
to_be_removed_bullets_right = []

# gets every bullet
for bullet in self.bullets_right:

    # checks any collision
    if bullet.colliderect(player):
        player.health -= 1
        to_be_removed_bullets_right.append(bullet)
        continue

    # Flag for noting the bullet was removed because Python doesn't have
    # labeled loops
    bullet_removed = False
    for tile in world.tile_list:
        if tile[1].colliderect(bullet):
            bullet_removed = True
            to_be_removed_bullets_right.append(bullet)
            break
    if bullet_removed:
        continue
        
    # Alternatively, you could do this
    # if any(tile[1].colliderect(bullet) for tile in world.tile_list):
    #      to_be_removed_bullets_right.append(bullet)
    #      continue

    if bullet.x > WIDTH:
        to_be_removed_bullets_right.append(bullet)
        continue

    # applies movement to bullet
    bullet.x += self.bullet_speed

    # applies gravity to bullet
    self.bullet_y_vel += self.bullet_grav
    bullet.y += self.bullet_y_vel
    p.draw.rect(WIN,self.bullet_colour,bullet)

# Remove bullets marked for removal
for bullet in to_be_removed_bullets_right:
    self.bullets_right.remove(bullet)
GeeTransit
  • 1,458
  • 9
  • 22