Pygame remove a single sprite from a group

14,086

Solution 1

You cannot remove items from a list while iterating over it:

def die(self):
    for enemy in EnemyList:
        if enemy.rect.x<100:
             EnemyList.remove(enemy)

You could instead write something like this:

def die(self):
     global EnemyList
     EnemyList = [enemy for enemy in EnemyList if enemy.rect.x>=100]

The global statement is needed so that the function can modify the EnemyList, which is outside its scope in this case.

Or store the enemies that you want to delete to another list, and then delete them afterwards.

Solution 2

An easier way would be to use the Sprite method kill() which removes a sprite from all pygame.sprite.Group´s it's currently in.

def die(self):
    if self.rect.x < 100:
        self.kill()

Calling die() in your update() will allow you to check whether to kill your sprite every game loop, but given the amount of code in die() you could simply just move the code inside the update(). And based on your current code, your EnemyList is already a pygame.sprite.Group object, so no change is needed there.

Your current die() method is quite hard for me to comprehend. You're creating an enemy which checks whether all enemies (itself and the other in the list) should die. So in your game loop you call the method from a single enemy in order to control all enemies. In other words, you're creating identical objects with different responsibilities. As a rule of thumb: an object's methods should change only its own states.

Lastly, the draw() method is unnecessary since the superclass defines the method exactly in the way you have. So removing yours would instead call draw() of pygame.sprite.Sprite.

Share:
14,086

Related videos on Youtube

Steve104
Author by

Steve104

Updated on June 04, 2022

Comments

  • Steve104
    Steve104 almost 2 years

    I am making a game and when the sprite goes under 100 on the x axis it is supposed to be deleted. The sprites are all located in a class. When the first sprite reaches the end, it deletes the final sprite of the group instead of the first.

    Enemy Class

    class Enemy(pygame.sprite.Sprite): 
        def __init__(self,x,y):
            pygame.sprite.Sprite.__init__(self)
            self.image = pygame.image.load('Enemy.gif').convert()
            self.rect = self.image.get_rect(x=x, y=y)
    
        def update(self):
           self.rect.x -= 4
    
        def die(self):
            for enemy in EnemyList:
                if enemy.rect.x<100:
                     EnemyList.remove(enemy)
    
        def draw(self, DISPLAY):
            DISPLAY.blit(self.image, self.rect)
    

    Main Loop (Part for enemy)

    time = 0
    while not Gameover: #Loop of the gameplay
    if time in (0,50,100,150,200):
        enemy = Enemy(DIS_HEIGHT,random.randrange(0,DIS_HEIGHT)
        enemy.add(EnemyList)
    EnemyList.update()
    EnemyList.draw(DISPLAY)
    enemy.die()
    time +=1
    

    I have a background and everything else works fine, I just cannot delete the correct sprite from the enemy group.

  • Steve104
    Steve104 over 7 years
    It doesn't work i get the error Traceback (most recent call last): File "F:\Game.py", line 203, in <module> enemy.die() File "F:\Game.py", line 53, in die EnemyList = [enemy for enemy in EnemyList if enemy.rect.x<100] UnboundLocalError: local variable 'EnemyList' referenced before assignment
  • J. P. Petersen
    J. P. Petersen over 7 years
    I assumed that EnemyList was a defined variable, as the code you posted used it already.
  • J. P. Petersen
    J. P. Petersen over 7 years
    Sorry :) forgot a global declaration. I'll update answer.
  • Steve104
    Steve104 over 7 years
    I did it like this return[EnemyList.remove(enemy) for enemy in EnemyList if enemy.rect.x<100]
  • J. P. Petersen
    J. P. Petersen over 7 years
    I've updated the answer now. Another solution without the global statement would be EnemyList[:] = ...
  • J. P. Petersen
    J. P. Petersen over 7 years
    Could you accept my answer if you found it useful? :)
  • sloth
    sloth over 7 years
    Yes, using kill is the way to go. But why not simply move this check into the update function?
  • Ted Klein Bergman
    Ted Klein Bergman over 7 years
    I figured it might be good to have them separated if we would like to add more checks, but given the current amount of code it probably would be better to put it directly in the update method. I'll edit it in, thanks!