0

I'm working on a side-scrolling shooter in Python and Pygame. At the beginning of play, I create a bunch of lists such as enemies[] and shots[]. When the game restarts, I have tried everything I can think of, but all the instances are still on the screen from the last playthrough!

In my restart code I have this...

for item in enemies:
    enemies.remove(item)
    del item
for item in shots:
    shots.remove(item)
    del item

I even have added code after that to reinitialize the list, so:

enemies=[]
shots=[]

But then on the new playthrough everything is still there. Help!

cal souza
  • 3
  • 2
  • possible duplicate of [How to empty a list in Python?](http://stackoverflow.com/questions/1400608/how-to-empty-a-list-in-python) – Peter Wood Apr 07 '15 at 17:41
  • 1
    When you do `enemies = []` and `shots = []`, you're relying on the garbage collector to delete all old instances for you. (i.o.w. you're relying on the objects to have no bindings, which is not necessarily true!) The best way to clear a list without creating a new one is using a splice assignment: `l[:] = []`. Alternatively: `del l[:]`. – Shashank Apr 07 '15 at 17:55
  • @shashank: while your proposed solution would work, your explanation for it is incorrect. This has nothing to do with "the garbage collector had not reached this objects yet". – jsbueno Apr 07 '15 at 18:55
  • @jsbueno Why does it have nothing to do with it? If an object is still in scope somewhere because its binded to by another variable (as you even mentioned in your answer), it won't be garbage collected. – Shashank Apr 07 '15 at 20:28
  • For some reason, when I try the splice assignment method l[:] = [], the game suddenly thinks that "global name "enemies" is not defined." – cal souza Apr 07 '15 at 22:09
  • @calsouza if `enemies` is global scope, you need to bring it into the scope of your function first: `global enemies` – Peter Wood Apr 08 '15 at 09:23
  • I know globals are the devil, but I already global'd it. It only freaks out when I try to slice. – cal souza Apr 08 '15 at 14:11
  • @Shashank: "Why does it have nothing to do with [garbage colector...]?" - because the game code fetches the objects from those lists (Infered from the behavior - code is not there for that). If they are no longer there, it does not matter they still exist elsewhere. – jsbueno Apr 08 '15 at 22:35

3 Answers3

2

Your main problem is that your lists do exist in more than one context (another method or another function)

When you do enemies = [] you clear the local variable named "enemies" - but it ceases to point to the previous object, which is used in another funtcion/method (although you don't list the code for that here).

Your other approach, using for tries to iterate on a list while modifying it - it should (and does) break in unexpected ways.

Simply do:

enemies[:] = []
shots[:] = []

instead: these will change the object currently referenced by those names, not create new objects and use them locally. This way, the contents on whatever other function that uses these lists will be emptied as well.(It is not magic: the [:] slice syntax addresses all elements of the list - from the first to the last, and the attribution makes those elements be replaced by the contents of the empty list: nothing)

While this will work for now, if you are using enemies, shots and other data structures across several functions/methods, maybe you'd have a cleaner design using a class, holding all your data structures as attributes of that class, and promote your functions to methods (even if otherwise you are not currently making other uses of an Object Oriented design). This way, it would be explicit that you are trying to modify the same objects that are used in other parts of the code.

Another advice: read the docs, and get familiarized with pygame Groups (and Sprites) - they are quite handy for a clean design, and certainly can perform better anything you are doing with your list objects above (for example, if enemies were a group, you'd just call enemies.empty() in this case)

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • This worked perfectly after I moved the variables from globals to their own class with their own methods. Thank you much, although, do you (or someone else) mind explaining why this was failing when I had them listed as global variables but worked fine in a class? What am I not understanding about how global variables work? – cal souza Apr 08 '15 at 14:42
  • @calsouza: If you have a list (or dictionary, or mutable object) and use it inside a function without declaring its name as "global" inside the function - Python will modify the global object anyway--unless there is an assignment inside the same function (even if later in the code) for those names. In this case, the compiler assumes those names to refer to local objects (and you probably did get a "NameError" exception when you first introduced the enemies=[] line, in a part of the code that did not have erros before. – jsbueno Apr 08 '15 at 22:39
  • http://www.python-course.eu/python3_global_vs_local_variables.php <- longer explanation on how globals work – jsbueno Apr 08 '15 at 22:40
0

Are you using Pygame objects such as Sprites and Groups?

Pygame has an object for Sprite storage called a Group. It has a number of convenience methods for hit detection, drawing, and adding/removing Sprites such that you should never have to delete anything.

In addition, Sprites have a self.kill() method which removes them from all Groups they belong to. In this way, it doesn't matter if a Sprite belongs to 1 Group or 703 Groups; it'll be removed from all of them.

Finally, none of the code you've posted seems to cover the actual drawing of anything to the screen. Is it possible that you're missing an instance of your objects that are in some kind of "draw-these-to-screen" sequence? If so, then trying to delete them in the way you're doing here won't necessarily remove them from every list, or from every relevant scope. Example:

>>> foo = {}
>>> bar = []
>>> bar.append(foo)
>>> del foo
>>> foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
>>> bar
[{}] # the object lives!

Ultimately you'll probably want to make use of the pygame.sprite.Sprite and pygame.sprite.Group objects to make life a little simpler - again, they have convenience methods for handling all this stuff so you don't have to seek and destroy every instance of an object when you don't need it anymore.

  • I'm not... the game is a test of my abilities and I wanted to do it "the hard way," ie. using only pygame's graphic capabilities and programming everything else from scratch. – cal souza Apr 07 '15 at 22:03
0

I'm sorry but my answer would depends on your complete code.

It mostly depends on how you call enemies and shots updating.
So, if you iterate through list and updates every objects this should work:

enemies = []
shots = []

But you said it doesn't work.
There are two most believable things you did wrong:

  1. enemies and shots aren't real iteration lists so you are updating them through some other list. If you are using additional libraries they might have their on lists for updating.
  2. You don't update old enemies nor shots, but you didn't fill your screen or something familiar.

Note that this answer would probably be better if you added more of your code and it still isn't late.

knowledge
  • 383
  • 3
  • 15