Reputation: 49
I have a game with classes "Oranges" and "Witches, which are sprites and class "MyGame". In function "draw" in "MyGame" class, the game draws oranges and witches, when user clicks on the witch, I need to remove witch from the list, so it should disappear from the screen. I've made it like this, and problem is it removes all witches from the list not only one of them, and in some cases I just have all of them disappear from the screen, and in another I have:
Traceback (most recent call last):
File "mygame.py", line 219, in <module>
MyGame().run()
File "mygame.py", line 155, in run
self.draw()
File "mygame.py", line 175, in draw
self.witches[i].draw(self.screen)
IndexError: list index out of range.
Here is my code:
class MyGame(object):
def __init__(self):
"""Initialize a new game"""
pygame.mixer.init()
pygame.mixer.pre_init(44100, -16, 2, 2048)
self.witches = []
for x in xrange(2):
position = self.width//2, self.height//2
self.witches.append(Witch(position, self.witch))
self.pos = 0, 0
def draw(self):
"""Update the display"""
# everything we draw now is to a buffer that is not displayed
self.screen.fill(self.bg_color)
for i in self.oranges:
i.draw(self.screen)
if len(self.witches) >= 2:
for i in range(len(self.witches)):
self.witches[i].draw(self.screen)
if int(self.witches[i].position[1]) in range(250,350):
for o in self.oranges:
self.oranges.remove(o)
if self.pos[0] in range (int(self.witches[i].position[0]-30), (int(self.witches[i].position[0])+30) \
or self.pos[1] in range ((int(self.witches[i].position[1])-30), (int(self.witches[i].position[1])+30))):
for w in self.witches:
print "witches out"
self.witches.remove(w)
P.S. I am beginner, can someone explain it easy? I'll be very appreciate
Upvotes: 0
Views: 1452
Reputation: 64158
Let's focus on the for
loop where the error is occurring from:
for i in range(len(self.witches)):
self.witches[i].draw(self.screen)
if int(self.witches[i].position[1]) in range(250,350):
for o in self.oranges:
self.oranges.remove(o)
if self.pos[0] in range (int(self.witches[i].position[0]-30), (int(self.witches[i].position[0])+30) \
or self.pos[1] in range ((int(self.witches[i].position[1])-30), (int(self.witches[i].position[1])+30))):
for w in self.witches:
print "witches out"
self.witches.remove(w)
If we pare down your code some more, it's basically doing this:
for i in range(len(self.witches)):
self.witches[i].draw(self.screen)
# Do stuff with oranges
if some_conditions_are_true:
for w in self.witches:
print "witches out"
self.witches.remove(w)
What's happening is that under some conditions, you're removing witches from your self.witches
list. However, the top for
loop is still iterating over range(len(self.witches))
-- it doesn't realize that the number of witches have change, and so it still iterating over the original amount.
Once you remove a single witch, the for-loop is going to continue iterating past the bounds of your list.
In addition, the for
loop is going to delete every witch, not just one that got close to the player.
Here's how I'd fix your code:
alive_witches = []
# This kind of for-loop lets you grab each element from a list.
# It's identical to how you were grabbing each witch before, but is cleaner to read
for witch in self.witches:
witch.draw(self.screen)
# Do stuff with oranges
witch_x = witch.position[0]
witch_y = witch.position[1]
close_to_player_x = witch_x - 30 <= self.pos[0] <= witch_x + 30
close_to_player_y = witch_y - 30 <= self.pos[1] <= witch_y + 30
if not (close_to_player_x or close_to_player_y):
# Save the witches that are not dead
alive_witches.append(witch)
# Save the witches that are still alive
self.witches = alive_witches
Looking at your code, you may have a similar problem with your oranges. I'll leave it as an exercise for you to figure out how to fix it, in that case.
Upvotes: 3