Forループが他のForループをブロックしている
ミサイルのインスタンスがenemyGroup内の敵のインスタンスと衝突したかどうかを確認するためにミサイルグループをチェックしています。実行すると、最初のループに対して「ヒット」が出力されますが、2番目のforループは無視されます。何故ですか?
#### 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")
更新:@ Rabbid76はspritecollide
、spriteGroupenemyGroup
がスプライトのグループ(enemyGroup <-Enemy(sprite))ではなく、グループ(enemyGroup <-enemyList <-Enemy(sprite))内のスプライトのリストであるため、機能しないと述べました。どうすればアクセスできますか?
アップデート2 @ paxdiabloは、最初のループが反復後にグループを空にする可能性があると述べています。ループの場所を切り替えて、2番目のループが実行されましたが、1番目は実行されませんでした。
更新3完全なコードでは、グループからスプライトを削除する.reset()
メソッドが実行さ.kill()
れます。最初のループは、2番目のループが衝突を検出できなくなる前にミサイルスプライトを削除するため、次のようになります。
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()
回答
参照pygame.sprite.spritecollide():
別のスプライトと交差するグループ内のすべてのスプライトを含むリストを返します。
したがって、への引数spritecollide()
はpygame.sprite.Spriteオブジェクトとオブジェクトでなければなりませんpygame.sprite.Group。グループの代わり
にpygame.sprite.Sprite
オブジェクトのリストが機能しません。
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")
さらに読む kill()
スプライトは、それを含むすべてのグループから削除されます。
したがってkill()
、1番目のループを呼び出すと、スプライトがすべてのグループから削除されるため、2番目のループは機能しません。
あなたは呼んkill()
でreset
メソッド。missile.reset()
それぞれeachEnemy.reset()
、2番目のループが失敗します。
提供された情報(a)に基づくと、2番目の衝突チェックが失敗する理由は明らかではありません。(たとえば)敵#7とミサイル#3の間に衝突がある場合、ミサイル#3と敵#7の間に衝突もあるはずです。
独自の(おそらく非可換)衝突関数を提供するようなエッジケースのものを使用していないので、これを検出するためにスプライト長方形を使用するだけです。
コード内の2つのループの順序を逆にしたときの動作を確認したいと思います。
また、これらのグループ変数のタイプを指定する必要があります。enemyGroup
リストではなく枯渇性ジェネレーターのようなものである場合、最初のループによって「空」になり、2番目のループには反復する項目がありません(b)(spritecollide
呼び出しは各項目をチェックするためにグループを反復しますスプライトに対して)。
これは、バグspritecollide
自体を除いて、説明している効果を確認する唯一の方法です。
例として、ジェネレーターを2回反復しようとするコードを次に示します。
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)
出力は、2番目のループが何もしないことを示しています。
A:
0
1
2
B:
最後に、グループの状態を確認する最も確実な方法は、各ループの前に次のコードを配置することです。
print("loop X enemy ", len(enemyGroup), enemyGroup)
print("loop X missile", len(missileGroup), missileGroup)
の適切な値を使用してX
、2つのループを区別します。
(a)もちろん、あなたが提供した情報が完全に正確または完全ではない可能性は常にあります(悪意のある意図は想定されていませんが、重要でない詳細と見なされるものを誤ってスキップすることがあり、それは非常に重要になります)。
例:問題の原因となっている2つのループの間に何かが起こっている可能性があります。私は人々に疑いの利益を与えることを望みます、しかしあなたはおそらくそれが事実であるかどうか私たちに知らせなければなりません。
(b)実際には、最初のループの最初の反復によって空になるため、おそらく最初のミサイルにのみ一致することがわかります。
これは、(PyGame 1.9.6で)この報告された動作が発生しないことを示す簡単な例です。
この例では、2つのスプライトグループを作成し、OPのサンプルコードとまったく同じ方法でそれらを衝突させます。
印刷だけでなく、スプライトは、衝突に参加したと信じているかどうかに応じて、アウトライン->塗りつぶしから変化します。ミサイルと衝突する敵の1:1マッピングがあり、その逆もあります。
フレームレートが悪いことをお詫びします...

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