简体   繁体   中英

Pygame: collision is removing sprite from group but still blitting to screen

I have two ships and a set of pills that should continuously rain down from the top of the screen. When my ships collides with a pill, the pills should disappear but they don't. The pills disappear further down the screen about 50 pixels from y=win_height.

After running some tests, I can say that the pills are detected by 'collidesprite' and they are removed from the group 'pillGroup', however they are still being blitted to the screen and pass right through the ship. I want the pills to disappear immediately.

Here is an edited version of my algorithm:

pillGroup = pygame.sprite.Group()

# Gameplay
while play:
    # Checks if window exit button pressed
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()

        # Keypresses

    pill = Pill()
    pill.add(pillGroup)

    pillGroup.update()
    pygame.sprite.spritecollide(ship_right, pillGroup, True)

    # Print background
    screen.fill(WHITE)
    pillGroup.draw(screen)

Here is the complete version of the game. Take note that I increased the ships sizes to fill the screen. Try moving the ships to the top of the screen and you'll see that the pills disappear when they get to the bottom of the screen rather than when first touching the ships.

import sys, pygame, os, random

# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'

# Runs imported module
pygame.init()

# Constants
LEFT = 'left'
RIGHT = 'right'

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

SHIP_WIDTH = 600
SHIP_HEIGHT = 10
PILL_WIDTH = 5
PILL_HEIGHT = 20

WIN_W = 1200
WIN_H = 570

RANDOM_PILL = [700 ,  658 ,  58 ,  795 ,  687 ,  308 ,  785 ,  828 ,  670 ,  187 ,  603 ,  994 ,  990 ,  923 ,  523 ,  849 ,  229 ,  804 ,  618 ,  733 ,  614 ,  209 ,  941 ,  872 ,  528 ,  438 ,  793 ,  879 ,  327 ,  539 ,  571 ,  886 ,  452 ,  627 ,  267 ,  662 ,  655 ,  772 ,  904 ,  769 ,  197 ,  1009 ,  281 ,  867 ,  31 ,  30 ,  545 ,  519 ,  866 ,  66 ,  202 ,  580 ,  1172 ,  792 ,  131 ,  980 ,  83 ,  999 ,  120 ,  916 ,  956 ,  1166 ,  391 ,  1127 ,  675 ,  868 ,  851 ,  725 ,  869 ,  702 ,  767 ,  692 ,  695 ,  564 ,  978 ,  834 ,  866 ,  340 ,  78 ,  396 ,  195 ,  480 ,  1113 ,  223 ,  725 ,  36 ,  660 ,  973 ,  597 ,  734 ,  1129 ,  91 ,  720 ,  610 ,  1020 ,  861 ,  887 ,  350 ,  235 ,  39 ,  282 ,  698 ,  856 ,  236 ,  1077 ,  191 ,  1147 ,  605 ,  825 ,  1179 ,  978 ,  320 ,  985 ,  125 ,  475 ,  1009 ,  166 ,  528 ,  100 ,  455 ,  485 ,  1066 ,  38 ,  408 ,  235 ,  410 ,  1064 ,  57 ,  653 ,  177 ,  836 ,  228 ,  224 ,  534 ,  789 ,  1129 ,  370 ,  1008 ,  529 ,  62 ,  752 ,  654 ,  785 ,  916 ,  281 ,  235 ,  921 ,  945 ,  727 ,  1138 ,  585 ,  168 ,  1048 ,  708 ,  144 ,  968 ,  974 ,  1118 ,  555 ,  251 ,  1135 ,  805 ,  1121 ,  360 ,  968 ,  228 ,  988 ,  49 ,  536 ,  117 ,  726 ,  65 ,  177 ,  925 ,  923 ,  239 ,  1117 ,  678 ,  329 ,  180 ,  1019 ,  47 ,  147 ,  235 ,  279 ,  841 ,  383 ,  211 ,  971 ,  309 ,  452 ,  335 ,  1121 ,  1091 ,  247 ,  790 ,  418 ,  961 ,  323 ,  458 ,  970 ,  752 ,  888 ,  719 ,  318 ,  930 ,  797 ,  537 ,  1006 ,  696 ,  309 ,  1102 ,  728 ,  497 ,  553 ,  266 ,  67 ,  504 ,  763 ,  158 ,  944 ,  108 ,  553 ,  517 ,  470 ,  1016 ,  93 ,  243 ,  570 ,  136 ,  67 ,  893 ,  241 ,  571 ,  515 ,  616 ,  986 ,  561 ,  148 ,  351 ,  78 ,  862 ,  685 ,  286 ,  414 ,  756 ,  730 ,  381 ,  141 ,  896]
PILL_COUNT = 0


class Entity(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

class Ship():
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = SHIP_WIDTH
        self.height = SHIP_HEIGHT
        self.speed = 5
        self.density = 0
        self.ship = pygame.Surface((self.width, self.height)).convert()
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        self.up = self.down = self.left = self.right= False

    def update(self, which_ship, left, right, down, up, pillGroup):
        # Adjust speed
        if up or down or left or right:
            if up:
                self.y -= self.speed
            if down:
                self.y += self.speed
            if left:
                self.x -= self.speed
            if right:
                self.x += self.speed

        # Ship movement
        if self.y < 0:
            self.y = 0
        if self.y > WIN_H - self.height:
            self.y = WIN_H - self.height
        if which_ship == 'left':
            if self.x < 0:
                self.x = 0
            if self.x > WIN_W/2 - self.width:
                self.x = WIN_W/2 - self.width
        elif which_ship == 'right':
            if self.x < WIN_W/2:
                self.x = WIN_W/2
            if self.x > WIN_W - self.width:
                self.x = WIN_W - self.width


class Pill(Entity):
    def __init__(self, density):
        Entity.__init__(self)
        global PILL_COUNT
        self.speed = 3
        self.density = density
        self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
        self.rect = self.image.get_rect()
        self.rect = self.rect.move(RANDOM_PILL[PILL_COUNT], -PILL_HEIGHT) #RANDOM_PILL[PILL_COUNT], PILL_HEIGHT)
        PILL_COUNT += 1

    def restart(self):
        pass

    def update(self):
        if self.rect.y > WIN_H:
            del self
        else:
            self.rect = self.rect.move((0, self.speed))


def genRandom():
    for i in range(250):
        print random.randrange(PILL_WIDTH, WIN_W - PILL_WIDTH), ", ",

def main():
    fps = 60

    pygame.display.set_caption('Pong')
    screen = pygame.display.set_mode((WIN_W, WIN_H), pygame.SRCALPHA)

    ship_left = Ship((WIN_W/4) - (SHIP_WIDTH/2), WIN_H - (SHIP_HEIGHT * 4))
    ship_right = Ship((WIN_W/1.3) - (SHIP_WIDTH/2), WIN_H - (SHIP_HEIGHT * 4))

    pillGroup = pygame.sprite.Group()

    pillAlter = 0

    vert_partition = pygame.Surface((1, WIN_H))
    hori_partition = pygame.Surface((WIN_W, 1))

    lLeft = lRight = lUp = lDown = False
    rLeft = rRight = rUp = rDown = False

    clock = pygame.time.Clock()
    play = True

    #ranList = genRandom()

    # Gameplay
    while play:
        # Checks if window exit button pressed
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()

            # Keypresses
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
                if event.key == pygame.K_a:
                    lLeft = True
                    lRight = False

                if event.key == pygame.K_d:
                    lLeft = False
                    lRight = True
                if event.key == pygame.K_w:
                    lDown = False
                    lUp = True
                if event.key == pygame.K_s:
                    lDown = True
                    lUp = False

                if event.key == pygame.K_LEFT:
                    rLeft = True
                    rRight = False
                if event.key == pygame.K_RIGHT:
                    rLeft = False
                    rRight = True
                if event.key == pygame.K_UP:
                    rDown = False
                    rUp = True
                if event.key == pygame.K_DOWN:
                    rUp = False
                    rDown = True

            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_a:
                    lLeft = False
                if event.key == pygame.K_d:
                    lRight = False
                if event.key == pygame.K_w:
                    lUp = False
                if event.key == pygame.K_s:
                    lDown = False

                if event.key == pygame.K_RIGHT:
                    rRight = False
                if event.key == pygame.K_LEFT:
                    rLeft = False
                if event.key == pygame.K_UP:
                    rUp = False
                if event.key == pygame.K_DOWN:
                    rDown = False

        if PILL_COUNT < 250 and pillAlter % 10 == 0:
            pill = Pill(random.randrange(1,4))
            pillGroup.add(pill)
        pillAlter += 1

        ship_left.update('left', lLeft, lRight, lDown, lUp, pillGroup)
        ship_right.update('right', rLeft, rRight, rDown, rUp, pillGroup)

        pillGroup.update()

        pill_hit_list = pygame.sprite.spritecollide(ship_right, pillGroup, True)
        pill_hit_list = pygame.sprite.spritecollide(ship_left, pillGroup, True)

        # Print background
        screen.fill(WHITE)
        print pillGroup.draw(screen)
        screen.blit(ship_left.ship, (ship_left.x, ship_left.y))
        screen.blit(ship_right.ship, (ship_right.x, ship_right.y))
        screen.blit(vert_partition, (WIN_W/2, WIN_H/15))
        screen.blit(hori_partition, (0, WIN_H/15))


        # Limits frames per iteration of while loop
        clock.tick(fps)
        # Writes to main surface
        pygame.display.flip()


if __name__ == "__main__":
    main()

The problem is that pygame.sprite.spritecollide checks the rect attribute of the sprite you pass as first parameter and compares it to the rect attribute of the sprites in the sprite group.

However, you don't actually change the rect attribute of your Ship instances.

Get rid of the x and y attributes of your Ship class and use only the rect attribute to store the position of the sprite.


Here's an updated version:

import sys, pygame, os, random

# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'

pygame.init()

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

SHIP_WIDTH = 30
SHIP_HEIGHT = 10
PILL_WIDTH = 5
PILL_HEIGHT = 20

WIN_W = 1200
WIN_H = 570

RANDOM_PILL = [700 ,  658 ,  58 ,  795 ,  687 ,  308 ,  785 ,  828 ,  670 ,  187 ,  603 ,  994 ,  990 ,  923 ,  523 ,  849 ,  229 ,  804 ,  618 ,  733 ,  614 ,  209 ,  941 ,  872 ,  528 ,  438 ,  793 ,  879 ,  327 ,  539 ,  571 ,  886 ,  452 ,  627 ,  267 ,  662 ,  655 ,  772 ,  904 ,  769 ,  197 ,  1009 ,  281 ,  867 ,  31 ,  30 ,  545 ,  519 ,  866 ,  66 ,  202 ,  580 ,  1172 ,  792 ,  131 ,  980 ,  83 ,  999 ,  120 ,  916 ,  956 ,  1166 ,  391 ,  1127 ,  675 ,  868 ,  851 ,  725 ,  869 ,  702 ,  767 ,  692 ,  695 ,  564 ,  978 ,  834 ,  866 ,  340 ,  78 ,  396 ,  195 ,  480 ,  1113 ,  223 ,  725 ,  36 ,  660 ,  973 ,  597 ,  734 ,  1129 ,  91 ,  720 ,  610 ,  1020 ,  861 ,  887 ,  350 ,  235 ,  39 ,  282 ,  698 ,  856 ,  236 ,  1077 ,  191 ,  1147 ,  605 ,  825 ,  1179 ,  978 ,  320 ,  985 ,  125 ,  475 ,  1009 ,  166 ,  528 ,  100 ,  455 ,  485 ,  1066 ,  38 ,  408 ,  235 ,  410 ,  1064 ,  57 ,  653 ,  177 ,  836 ,  228 ,  224 ,  534 ,  789 ,  1129 ,  370 ,  1008 ,  529 ,  62 ,  752 ,  654 ,  785 ,  916 ,  281 ,  235 ,  921 ,  945 ,  727 ,  1138 ,  585 ,  168 ,  1048 ,  708 ,  144 ,  968 ,  974 ,  1118 ,  555 ,  251 ,  1135 ,  805 ,  1121 ,  360 ,  968 ,  228 ,  988 ,  49 ,  536 ,  117 ,  726 ,  65 ,  177 ,  925 ,  923 ,  239 ,  1117 ,  678 ,  329 ,  180 ,  1019 ,  47 ,  147 ,  235 ,  279 ,  841 ,  383 ,  211 ,  971 ,  309 ,  452 ,  335 ,  1121 ,  1091 ,  247 ,  790 ,  418 ,  961 ,  323 ,  458 ,  970 ,  752 ,  888 ,  719 ,  318 ,  930 ,  797 ,  537 ,  1006 ,  696 ,  309 ,  1102 ,  728 ,  497 ,  553 ,  266 ,  67 ,  504 ,  763 ,  158 ,  944 ,  108 ,  553 ,  517 ,  470 ,  1016 ,  93 ,  243 ,  570 ,  136 ,  67 ,  893 ,  241 ,  571 ,  515 ,  616 ,  986 ,  561 ,  148 ,  351 ,  78 ,  862 ,  685 ,  286 ,  414 ,  756 ,  730 ,  381 ,  141 ,  896]
PILL_COUNT = 0

class Entity(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

class Ship(Entity):
    def __init__(self, container):
        Entity.__init__(self)
        self.speed = 5
        self.density = 0
        self.image = pygame.Surface((SHIP_WIDTH, SHIP_HEIGHT)).convert()
        self.rect = self.image.get_rect(center=container.center)
        self.container = container

    def update(self):
        self.rect.clamp_ip(self.container)

    def move(self, vector):
        self.rect.move_ip([self.speed * a for a in vector])

class Pill(Entity):
    def __init__(self, density):
        Entity.__init__(self)
        global PILL_COUNT
        self.speed = 3
        self.density = density
        self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
        self.rect = self.image.get_rect(topleft=(RANDOM_PILL[PILL_COUNT], -PILL_HEIGHT))
        PILL_COUNT += 1

    def update(self):
        self.rect.move_ip((0, self.speed))

        if self.rect.y > WIN_H:
            self.kill()

def main():
    fps = 60

    pygame.display.set_caption('Pong')
    screen = pygame.display.set_mode((WIN_W, WIN_H), pygame.SRCALPHA)

    ship_left = Ship(pygame.rect.Rect(0, 0, WIN_W/2, WIN_H))
    ship_right = Ship(pygame.rect.Rect(WIN_W/2, 0, WIN_W/2, WIN_H))

    pillGroup = pygame.sprite.Group()
    shipGroup = pygame.sprite.Group(ship_left, ship_right)

    pillAlter = 0

    vert_partition = pygame.Surface((1, WIN_H))
    hori_partition = pygame.Surface((WIN_W, 1))

    clock = pygame.time.Clock()
    play = True

    movement = {ship_left:  { pygame.K_w: ( 0, -1),
                              pygame.K_s: ( 0,  1),
                              pygame.K_a: (-1,  0),
                              pygame.K_d: ( 1,  0)},
                ship_right: { pygame.K_UP:    ( 0, -1),
                              pygame.K_DOWN:  ( 0,  1),
                              pygame.K_LEFT:  (-1,  0),
                              pygame.K_RIGHT: ( 1,  0)}}

    while play:
        for event in pygame.event.get():
            if event.type == pygame.QUIT: 
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit()


        pressed = pygame.key.get_pressed()
        for ship in shipGroup:
            for key, vector in movement[ship].iteritems():
                if pressed[key]:
                    ship.move(vector) #TODO: use real vector math

        if PILL_COUNT < 250 and pillAlter % 10 == 0:
            pill = Pill(random.randrange(1,4))
            pillGroup.add(pill)
        pillAlter += 1

        pillGroup.update()
        shipGroup.update()

        pygame.sprite.groupcollide(shipGroup, pillGroup, False, True)

        # Print background
        screen.fill(WHITE)
        pillGroup.draw(screen)
        shipGroup.draw(screen)

        screen.blit(vert_partition, (WIN_W/2, WIN_H/15))
        screen.blit(hori_partition, (0, WIN_H/15))

        clock.tick(fps)
        pygame.display.flip()


if __name__ == "__main__":
    main()

Note how the movement code is simplified. I also used kill() to actually remove the pills that go out of screen and used some features of the Rect class, like setting the initial position, moving in place ( move_ip ) and ensuring the Rect does not leave a specific area ( clamp_ip ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM