Reputation: 93
Im my pygame game I am comparing this distance between 2 objects, and trying to "pop" them when they are too close together, but sometimes, (seemingly random) the code crashes upon collision. if someone could help me, that would be nice.
the error message is:
IndexError: list index out of range
relevant code:
for i in reversed(range(len(birdies))):
birdies[i].moveBird()
birdies[i].calcSpeeds()
birdies[i].drawEnemyBird()
for j in reversed(range(len(shots))):
if distance(birdies[i].x, birdies[i].y, shots[j].x, shots[j].y) < shots[j].rad/2 + birdies[i].width/2:
birdies.pop(i)
shots.pop(j)
Upvotes: 3
Views: 48
Reputation: 210878
I will suggest an alternative solution.
Since the lists are iterated in reverse order, the only issue is birdies.pop(i)
in the inner loop, as pointed out by @Kingsley.
Add a state pop_bird
, in the outer loop and set the state in the inner loop when a birs has to be removed:
for i in reversed(range(len(birdies))):
birdies[i].moveBird()
birdies[i].calcSpeeds()
birdies[i].drawEnemyBird()
pop_bird = False
for j in reversed(range(len(shots))):
if distance(birdies[i].x, birdies[i].y, shots[j].x, shots[j].y) < shots[j].rad/2 + birdies[i].width/2:
pop_bird = True
shots.pop(j)
if pop_bird:
birdies.pop(i)
Possibly it is sufficient, to break
the inner loop, if a bird has been hit, because a bird cannot be shot twice:
for i in reversed(range(len(birdies))):
birdies[i].moveBird()
birdies[i].calcSpeeds()
birdies[i].drawEnemyBird()
for j in reversed(range(len(shots))):
if distance(birdies[i].x, birdies[i].y, shots[j].x, shots[j].y) < shots[j].rad/2 + birdies[i].width/2:
birdies.pop(i)
shots.pop(j)
break
Upvotes: 1
Reputation: 14906
The issue is that the code is iterating over a list, sometimes removing entries, but then expecting them to be there next iteration.
for j in reversed(range(len(shots))):
if <collision>:
birdies.pop(i) # <-- HERE
shots.pop(j)
Say a birdies
element is removed. The range of birdies is not re-computed, so on the next loop through shots
, it's still trying to examine the element it just removed.
Can you loop this some other way?
hit_birds = []
for b in birdies:
...
for s in shots:
if ( shotHits( b, s ) ): # TODO: write this function
hit_birds.append( b ) # just record which birds are hit
# remove shot birds
birdies = [ b for b in birdies if b not in hit_birds ]
It might also be easier in the long-run to encapsulate all the bird data into a list or class, and simply set the bird.hit
flag during the iteration... or suchlike.
Upvotes: 1