다른 For 루프를 차단하는 For 루프

Nov 29 2020

missileGroup을 확인하여 미사일 인스턴스가 적군의 인스턴스 적과 충돌했는지 확인합니다. 실행되면 첫 번째 루프에 대해 "Hit"을 인쇄하지만 두 번째 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은 spritecollidespriteGroup enemyGroup이 스프라이트 그룹 (enemyGroup <-Enemy (sprite))이 아닌 그룹 (enemyGroup <-enemyList <-Enemy (sprite)) 내의 스프라이트 목록 이기 때문에 작동하지 않을 것이라고 말했습니다 . 어떻게 액세스 할 수 있습니까?

업데이트 2 @paxdiablo는 첫 번째 루프가 반복 후 그룹을 비울 수 있다고 말했습니다. 나는 루프의 위치를 ​​바꾸고 두 번째 루프가 실행되었지만 첫 번째 루프는 실행되지 않았습니다.

업데이트 3 전체 코드 에서 그룹에서 스프라이트를 제거하는 .reset()메서드가 실행됩니다 .kill(). 첫 번째 루프는 두 번째 루프가 충돌을 감지하지 못하기 전에 미사일 스프라이트를 제거하기 때문에 :

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

답변

Rabbid76 Nov 29 2020 at 21:21

참조 pygame.sprite.spritecollide():

다른 Sprite와 교차하는 그룹의 모든 Sprite를 포함하는 목록을 반환합니다.

따라서 인수 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()첫 번째 루프 를 호출 하면 스프라이트가 모든 그룹 에서 제거되기 때문에 두 번째 루프가 작동하지 않습니다 .

당신은 전화 kill()reset방법. missile.reset()각각 eachEnemy.reset()두 번째 루프가 실패합니다.

paxdiablo Nov 29 2020 at 07:26

제공된 정보 (a)를 기반으로 두 번째 충돌 검사가 실패해야하는 이유는 분명 하지 않습니다. 예를 들어 적 # 7과 미사일 # 3 사이에 충돌이 발생하면 미사일 # 3과 적 # 7 사이 에도 충돌이 발생 해야합니다 .

당신은 자신의 (비교 류적) 충돌 함수를 제공하는 것과 같은 엣지 케이스를 사용하지 않으므로 단순히 스프라이트 직사각형을 사용하여 이것을 감지합니다.

코드에서 두 루프의 순서 를 반대로 하면 동작을보고 싶습니다 .


또한 이러한 그룹 변수의 유형을 지정해야합니다. enemyGroup목록이 아니라 소진 가능한 생성기와 같은 것이면 첫 번째 루프에 의해 "비워지고"두 번째 루프에는 반복 할 항목이 없습니다 (b) ( spritecollide호출은 각 항목을 확인하기 위해 그룹을 반복합니다. 스프라이트에 대해).

그것은 spritecollide당신이 설명하는 효과를 볼 수 있는 유일한 방법이며 버그 그 자체가 부족 합니다.


예를 들어, 다음은 생성기를 두 번 반복하는 코드입니다.

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)

출력은 두 번째 루프가 아무 작업도 수행하지 않음을 보여줍니다.

A:
  0
  1
  2
B:

마지막으로 그룹의 상태를 확인하는 확실한 방법은 각 루프 앞에 다음 코드를 추가하는 것입니다.

print("loop X enemy  ", len(enemyGroup),   enemyGroup)
print("loop X missile", len(missileGroup), missileGroup)

적절한 값을 사용 X하여 두 루프를 구분합니다.


(a) 물론, 귀하가 제공 한 정보가 완전히 정확하지 않거나 완전하지 않을 가능성 은 항상 있습니다 (악의적 인 의도는 없지만 때때로 사람들은 중요하지 않은 세부 사항으로 간주 되는 내용을 실수로 건너 뛰어 매우 중요하게됩니다).

예 : 문제를 일으키는 두 루프 사이에 어떤 일이 발생할 수 있습니다 . 사람들에게 의심의 혜택을주고 싶지만 그럴 경우 알려 주셔야합니다.


(b) 실제로 첫 번째 루프의 첫 번째 반복 에 의해 비워 지므로 아마도 첫 번째 미사일에만 일치 할 것임을 알 수 있습니다.

Kingsley Nov 29 2020 at 07:51

다음은이보고 된 동작이 발생하지 않음을 보여주는 간단한 예입니다 (PyGame 1.9.6에서).

이 예는 두 개의 생성 스프라이트 그룹을 그런 다음에 그들을 충돌 정확한 영업 이익의 예제 코드와 동일한 방법.

프린팅과 마찬가지로 스프라이트는 충돌에 참여했다고 믿는지 여부에 따라 윤곽선-> 채워진 상태에서 변경됩니다. 미사일과 충돌하는 적의 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()