Boucle For bloquant une autre boucle For
Je vérifie missileGroup pour voir si des instances de missile sont entrées en collision avec des instances ennemies dans l'ennemiGroup. Lorsqu'il est exécuté, il imprime "Hit" pour la première boucle, mais il ignore la deuxième boucle for. Pourquoi donc?
#### Imagine this is in a game loop ####
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False) :
print("Hit")
for enemy in enemyGroup:
if pygame.sprite.spritecollide(enemy, missileGroup, False):
print("HI")
Mise à jour : @ Rabbid76 a déclaré que spritecollide
cela ne fonctionnerait pas car le spriteGroup enemyGroup
est une liste de sprites au sein d'un groupe (ennemiGroup <- ennemiList <- Enemy (sprite)) au lieu d'un groupe de sprites (ennemiGroup <- Enemy (sprite)). Comment pourrais-je y accéder?
La mise à jour 2 @paxdiablo a déclaré que la première boucle vidait peut-être le groupe après l'itération. J'ai changé les emplacements des boucles et la 2ème boucle s'est déroulée, alors que la 1ère ne l'a pas fait.
Mise à jour 3 Dans le code complet, la .reset()
méthode s'exécute .kill()
qui supprime le sprite du groupe. Puisque la première boucle supprime le sprite de missile avant que la deuxième boucle ne puisse détecter de collisions:
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False) :
missile.reset()
for eachEnemy in enemyGroup:
if pygame.sprite.spritecollide(eachEnemy, missileGroup, False):
eachEnemy.reset()
Réponses
Voir pygame.sprite.spritecollide():
Renvoie une liste contenant tous les Sprites d'un groupe qui se croisent avec un autre Sprite.
Par conséquent, les arguments spritecollide()
doivent être un pygame.sprite.Spriteobjet et un pygame.sprite.Groupobjet.
Une liste d' pygame.sprite.Sprite
objets au lieu du groupe ne fonctionne pas.
missileGroup = pygame.sprite.Group()
enemyGroup = pygame.sprite.Group()
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False):
print("Hit")
for enemy in enemyGroup:
if pygame.sprite.spritecollide(enemy, missileGroup, False):
print("HI")
En outre, lisez sur kill()
Le Sprite est supprimé de tous les groupes qui le contiennent.
Par conséquent, si vous appelez kill()
la première boucle, la deuxième boucle ne fonctionnera pas, car le sprite est supprimé de tous les groupes.
Vous appelez kill()
les reset
méthodes. provoque missile.reset()
respectivement eachEnemy.reset()
l'échec de la 2ème boucle.
Il n'y a aucune raison évidente, basée sur les informations fournies (a) , pour laquelle la deuxième vérification de collision échoue. S'il y a une collision entre (par exemple) l'ennemi n ° 7 et le missile n ° 3, il devrait également y avoir une collision entre le missile n ° 3 et l'ennemi n ° 7.
Vous n'utilisez pas de trucs de cas de bord comme fournir votre propre fonction de collision (éventuellement non commutative), donc il utilisera simplement le rectangle de sprite pour détecter cela.
Je serais curieux de voir le comportement lorsque vous inversez l'ordre des deux boucles dans le code.
Vous devez également spécifier les types de ces variables de groupe. S'il y enemyGroup
avait quelque chose comme un générateur épuisable plutôt qu'une liste, il serait «vidé» par la première boucle, puis la deuxième boucle n'aurait aucun élément à parcourir (b) (l' spritecollide
appel sera itéré sur le groupe pour vérifier chaque élément contre le sprite).
C'est à peu près le seul moyen, à moins d'un bogue en spritecollide
soi, que vous puissiez voir les effets que vous décrivez.
À titre d'exemple, voici un morceau de code qui essaie d'itérer deux fois sur un générateur:
class gen3(object):
def __init__(self): self._num = 0
def __iter__(self): return self
def __next__(self):
if self._num == 3: raise StopIteration()
self._num += 1
return self._num - 1
gen = gen3()
print("A: ")
for i in gen: print(" ", i)
print("B: ")
for i in gen: print(" ", i)
La sortie montre que la deuxième boucle ne fait rien:
A:
0
1
2
B:
Enfin, un moyen définitif de vérifier l'état des groupes est de simplement mettre le code suivant avant chaque boucle:
print("loop X enemy ", len(enemyGroup), enemyGroup)
print("loop X missile", len(missileGroup), missileGroup)
en utilisant une valeur appropriée de X
pour distinguer les deux boucles.
(a) Bien sûr, il est toujours possible que les informations que vous avez fournies ne soient pas totalement exactes ou complètes (aucune intention malveillante n'est supposée, mais parfois les gens sautent par inadvertance ce qu'ils considèrent comme des détails sans importance, cela finit par être très important).
Exemple: il se peut qu'il se passe quelque chose entre ces deux boucles, ce qui cause le problème. Je préférerais donner aux gens le bénéfice du doute, mais vous devriez probablement nous le faire savoir si c'est le cas.
(b) Il serait en fait vidé par la première itération de la première boucle, de sorte que vous constateriez qu'il ne correspondrait probablement que pour le premier missile.
Voici un exemple rapide qui montre (dans PyGame 1.9.6) ce comportement signalé ne se produit pas.
L'exemple crée deux groupes de sprites , puis les met en collision exactement de la même manière que l'exemple de code de l'OP.
En plus de l'impression, les sprites changent de contour -> rempli, selon qu'ils croient avoir participé à une collision. Il existe une cartographie 1: 1 d'un ennemi entrant en collision avec un missile, et vice-versa.
Toutes mes excuses pour la faible fréquence d'images ...

import pygame
import random
# Window size
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 800
DARK_BLUE = ( 3, 5, 54 )
RED = ( 200, 0, 0 )
YELLOW = ( 240, 250, 0 )
BLACK = ( 0, 0, 0 )
GREY = ( 200, 200, 200 )
GREEN = ( 250, 0, 0 )
TRANSPARENT=( 0,0,0,0 )
class Shape(pygame.sprite.Sprite):
def __init__(self, width=48, height=48):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface( ( width, height ), pygame.SRCALPHA)
self.rect = self.image.get_rect()
# Start position is randomly across the screen, and a little off the top
self.rect.center = ( random.randrange( 0, WINDOW_WIDTH ), random.randrange( 0, WINDOW_HEIGHT ) )
# Movement
self.dx = random.randrange( -2, 2 )
self.dy = random.randrange( -2, 2 )
# Looks like
self.filled = 2
self.last_fill = -1
self.render()
def setFilled( self, value ):
if ( value == True ):
self.filled = 0
else:
self.filled = 2
def update( self ):
if ( self.last_fill != self.filled ):
self.last_fill = self.filled
self.render()
self.rect.move_ip( self.dx, self.dy )
if ( self.rect.left > WINDOW_WIDTH ):
self.rect.x = -self.rect.width
elif ( self.rect.right < 0 ):
self.rect.left = WINDOW_WIDTH
if ( self.rect.y > WINDOW_HEIGHT ):
self.rect.y = 0
elif ( self.rect.y < 0 ):
self.rect.y = WINDOW_HEIGHT
class Square( Shape ):
def render( self ):
# Something to draw
if ( self.filled == 0 ):
self.image.fill( RED )
else:
border=3
x, y = border, border
width = self.rect.width - border -1
height = self.rect.height - border -1
self.image.fill( TRANSPARENT )
pygame.draw.rect( self.image, RED, (x,y,width,height), self.filled )
class Circle( Shape ):
def render( self ):
self.image.fill( TRANSPARENT )
pygame.draw.circle( self.image, YELLOW, (self.rect.width//2, self.rect.height//2), self.rect.width//2, self.filled )
### initialisation
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
### Some sprite groups
missileGroup = pygame.sprite.Group()
for i in range( 3 ):
new_missile = Circle()
new_missile.render()
missileGroup.add( Circle() )
enemyGroup = pygame.sprite.Group()
for i in range( 12 ):
new_enemy = Square()
new_enemy.render()
enemyGroup.add( Square() )
### Main Loop
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.MOUSEBUTTONUP ):
# On mouse-click
pass
# Move * collide the sprites
missileGroup.update()
enemyGroup.update()
# Test Collisions
for missile in missileGroup:
if pygame.sprite.spritecollide(missile, enemyGroup, False) :
print("Missile " + str(missile) + " Hits Enemy")
missile.setFilled( True )
else:
missile.setFilled( False )
for enemy in enemyGroup:
if pygame.sprite.spritecollide(enemy, missileGroup, False):
print("Enemy " + str(enemy) + " Hits Missile")
enemy.setFilled( True )
else:
enemy.setFilled( False )
# Paint the window, but not more than 60fps
window.fill( DARK_BLUE )
enemyGroup.draw( window )
missileGroup.draw( window )
pygame.display.flip()
# Clamp FPS
clock.tick(60)
pygame.quit()