For Loop blokuje inną pętlę For
Sprawdzam grupę pocisków, aby zobaczyć, czy jakiekolwiek wystąpienia pocisku nie zderzyły się z jakąkolwiek instancją wroga w grupie przeciwników. Po uruchomieniu drukuje "Hit" dla pierwszej pętli, ale ignoruje drugą pętlę for. Dlaczego?
#### 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")
Aktualizacja : @ Rabbid76 stwierdził, że spritecollide
to nie zadziała, ponieważ spriteGroup enemyGroup
to lista sprite'ów w grupie (wrogiej grupie <- wrogaLista <- Wróg (sprite)) zamiast grupy duszków (wrogaGroup <- Wróg (sprite)). Jak miałbym do tego uzyskać dostęp?
Update 2 @paxdiablo stwierdził, że pierwsza pętla może opróżnić grupę po iteracji. Zamieniłem miejsca pętli i biegła druga pętla, podczas gdy pierwsza nie.
Aktualizacja 3 W pełnym kodzie zostaje .reset()
uruchomiona metoda, .kill()
która usuwa duszka z grupy. Ponieważ pierwsza pętla usuwa duszkę pocisku, zanim druga pętla nie wykryje kolizji:
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()
Odpowiedzi
Zobacz pygame.sprite.spritecollide():
Zwróć listę zawierającą wszystkie duszki w grupie, które przecinają się z innym duchem.
Dlatego argumenty spritecollide()
muszą być pygame.sprite.Spriteprzedmiotem i pygame.sprite.Groupprzedmiotem.
Lista pygame.sprite.Sprite
obiektów zamiast grupy nie działa.
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")
Przeczytaj również o kill()
Sprite jest usuwany ze wszystkich grup, które go zawierają.
Dlatego jeśli wywołasz kill()
w pierwszej pętli, druga pętla nie zadziała, ponieważ duszek zostanie usunięty ze wszystkich grup.
Dzwonisz kill()
w reset
metodach. missile.reset()
odpowiednio eachEnemy.reset()
powoduje niepowodzenie drugiej pętli.
Na podstawie dostarczonych informacji (a) nie ma oczywistego powodu, dla którego druga kontrola kolizji miałaby się nie powieść. W przypadku kolizji (na przykład) przeciwnika nr 7 i pocisku nr 3, powinno również dojść do zderzenia pocisku nr 3 z wrogiem nr 7.
Nie używasz żadnych skrajnych rzeczy, takich jak dostarczanie własnej (prawdopodobnie nieprzemiennej) funkcji kolizji, więc po prostu użyje prostokąta sprite, aby to wykryć.
Byłbym ciekawy, jak zachowuje się odwrócenie kolejności dwóch pętli w kodzie.
Powinieneś także określić typy tych zmiennych grupowych. Gdyby enemyGroup
był czymś w rodzaju wyczerpującego się generatora, a nie listy, zostałby "opróżniony" przez pierwszą pętlę, a następnie druga pętla nie miałaby elementów do iteracji po (b) ( spritecollide
wywołanie będzie iterować po grupie, aby sprawdzić każdy element przeciwko duszkowi).
To prawie jedyny sposób, pomijając błąd spritecollide
sam w sobie, aby zobaczyć opisywane efekty.
Na przykład, oto fragment kodu, który dwukrotnie próbuje iterować po generatorze:
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)
Wynik pokazuje, że druga pętla nic nie robi:
A:
0
1
2
B:
Ostatnim sposobem sprawdzenia stanu grup jest po prostu umieszczenie następującego kodu przed każdą pętlą:
print("loop X enemy ", len(enemyGroup), enemyGroup)
print("loop X missile", len(missileGroup), missileGroup)
używając odpowiedniej wartości X
do rozróżnienia między dwiema pętlami.
(a) Oczywiście zawsze istnieje możliwość , że podane przez Ciebie informacje nie są w pełni dokładne lub kompletne (nie zakłada się złego zamiaru, ale czasami ludzie nieumyślnie pomijają to, co uważają za nieważne szczegóły, które ostatecznie okazują się bardzo ważne).
Przykład: coś może się dziać między tymi dwoma pętlami, co jest przyczyną problemu. Wolałbym dać ludziom korzyść w postaci wątpliwości, ale prawdopodobnie powinieneś nas o tym powiadomić.
(b) W rzeczywistości zostałby opróżniony przez pierwszą iterację pierwszej pętli, więc okaże się, że prawdopodobnie pasowałby tylko do pierwszego pocisku.
Oto krótki przykład pokazujący (w PyGame 1.9.6) to zgłoszone zachowanie nie występuje.
Przykład tworzy dwie grupy sprite , a następnie zderza je w dokładnie taki sam sposób jak przykład kodem OP.
Oprócz drukowania, duszki zmieniają się z konturów -> wypełnione, w zależności od tego, czy uważają, że uczestniczyły w kolizji. Istnieje mapowanie 1: 1 wroga zderzającego się z pociskiem i odwrotnie.
Przepraszamy za kiepską liczbę klatek na sekundę ...

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()