pygame sprite.Group - passing arguments out of a Group

Viewed 303

I'm trying out pygame, using Sprites & Groups, and getting myself a little confused around passing arguments.

Basically I have a wrapper class MyGame() as my_game(). In that I have pygame.sprite.Group() - all_sprites.

I then have a class Alien(pygame.sprite.Sprite) for my aliens, and I add each alien to my all_sprites Group.

I want my Group of aliens to track across the screen (which they do) and then all drop as a block, a group (which they don't).

I can loop through my all_sprites.Group in my main loop after the self.all_sprites.update() and see if alien.rect.right > WIDTH or alien.rect.left < 0 , then loop through the Group again calling alien.end_row() then break the loop so it doesn't run for each alien on the screen edge, but that seems very clunky.

I've tried setting a flag in MyGame self.alien_drop = False but when I try to set my_game.alien_drop = True in the Alien class, it doesn't recognise my_game - not defined. I'm a little confused as MyGame is creating the instances of Alien, so they should be enclosed by the scope of MyGame?

I can pass MyGame self.alien_drop into my Alien init() but when I update the Alien self.alien_drop it doesn't update MyGame.alien_drop. Which I get because it's created a new variable local to Alien.

There doesn't seem to be a way to pass an argument through Group.update() which I guess is because it's just calling the .update() on all Sprite inside the group. I can't see an easy way to modify the Group.update() function so that I can pass values, and in fairness I probably don't want to go mucking around in there anyway.

I also can't return True back through update().

I'm kinda stuck at this point...

I know the self.aliens.row_end() probably won't work, I'll have to loop through self.aliens and call each alien.row_end(), but at the moment it's not even getting to that point.

import pygame

WIDTH = 360 # width of our game window
HEIGHT = 480 # height of our game window
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)

class MyGame():
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        self.clock = pygame.time.Clock()
        self.all_sprites = pygame.sprite.Group()
        self.alien_drop = False
        for row_index in range(4):
            for column_index in range(6):
                alien = Alien(20 + (column_index * 40), 20 + (row_index *40))
                alien.add(self.all_sprites)
        while True:
            self.screen.fill(BLACK)
            self.all_sprites.update()
            if self.alien_drop:
                self.aliens.row_end()
            self.all_sprites.draw(self.screen)
            pygame.display.update()
            self.clock.tick(60)

class Alien(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((25, 25))
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.image.fill(GREEN)
        self.dx = 1

    def update(self):
        self.rect.x += self.dx
        if self.rect.right >= WIDTH or self.rect.left <= 0:
            my_game.alien_drop = True

    def row_end(self):
        self.dx *= -1
        self.rect.y += 40

if __name__ == "__main__":
    my_game = MyGame()
1 Answers

So to do the traditional Space Invaders move and drop, the entire row drops when any of the invaders hits either side.

In the example below I have modified the Alien.update() to detect when the screen-side is hit, but if-so, only set a Boolean flag Alien.drop to True.

The algorithm becomes: First move all the aliens. Next, check if any alien hit the side-wall by checking this flag. If so, move every alien down 1 step & stop checking.

The Alien.row_end() function moves the aliens down. It also needs to clear the Alien.drop flag, so they don't all move down again.

import pygame

WIDTH = 360 # width of our game window
HEIGHT = 480 # height of our game window
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)

class MyGame():
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        self.clock = pygame.time.Clock()
        self.all_sprites = pygame.sprite.Group()
        self.alien_drop = False
        for row_index in range(4):
            for column_index in range(6):
                alien = Alien(20 + (column_index * 40), 20 + (row_index *40))
                alien.add(self.all_sprites)

        exiting = False
        while not exiting:
            # Handle events
            for event in pygame.event.get():
                if ( event.type == pygame.QUIT ):
                    exiting = True  # exit this loop

            self.screen.fill(BLACK)
            self.all_sprites.update()
            for alien1 in self.all_sprites:
                if ( alien1.shouldDrop() ):
                    ### If any alien drops, we all drop
                    for alien2 in self.all_sprites:
                        alien2.row_end()
                    break  # only drop once

            if self.alien_drop:
                self.aliens.row_end()
            self.all_sprites.draw(self.screen)
            pygame.display.update()
            self.clock.tick(60)

class Alien(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((25, 25))
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.image.fill(GREEN)
        self.dx = 1
        self.drop = False  # should I drop-down a level

    def update(self):
        self.rect.x += self.dx
        if self.rect.right >= WIDTH or self.rect.left <= 0:
            self.drop = True
        else:
            self.drop = False

    def shouldDrop( self ):
        """ In this alien in a position where it should drop down
            one row in the screen-space (i.e.: it's hit a side)  """
        return self.drop

    def row_end(self):
        self.dx *= -1
        self.rect.y += 40
        self.drop = False    # drop drop again just yet

if __name__ == "__main__":
    my_game = MyGame()
Related