Pygame Shmup Part 2: Enemy Sprites

Tags: python tutorial gamedev pygame

This is part 2 of our “Shmup” project! In this lesson we’ll add some enemy sprites for our player to dodge. In this series of lessons we’ll build a complete game using Python and Pygame. It’s intended for beginning programmers who already understand the basics of Python and are looking to deepen their Python understanding and learn the fundamentals of programming games.

You can watch a video version of this lesson here:

Enemy Sprites

At this point, we don’t need to worry about what our enemy sprites are, we just want to get them on the screen. You might decide your game is about a spaceship dodging meteors or a unicorn dodging flying pizzas - as far as the code is concerned, it doesn’t matter.

Keeping that in mind, we’re going to name our enemy sprite in the code something generic. It turns out there’s a perfect word for generic objects that move around in your game (a term you may have heard before): Mob.

We’ll start by defining the sprite properties:

class Mob(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((30, 40))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(WIDTH - self.rect.width)
        self.rect.y = random.randrange(-100, -40)
        self.speedy = random.randrange(1, 8)

The key here is picking a good spot for the mobs to spawn. We don’t want them to just pop into existence, so we pick a y value that will be above the top (y < 0) and a random value for x that is somewhere between the sides.

Now, for the update, we can move the sprite by its speedy, but what about when the sprite goes off the bottom? We could delete the sprite, and then spawn another one, or we could get the exact same effect by moving the sprite back to a random position above the top:

def update(self):
        self.rect.y += self.speedy
        if self.rect.top > HEIGHT + 10:
            self.rect.x = random.randrange(WIDTH - self.rect.width)
            self.rect.y = random.randrange(-100, -40)
            self.speedy = random.randrange(1, 8)

Spawning Enemies

We are going to want to have many enemies, so we’re going to make a new group called mobs to hold them all. This is also going to make our lives easier in later steps. Then we spawn a number of mobs and add them to the groups:

player = Player()
all_sprites.add(player)
for i in range(8):
    m = Mob()
    all_sprites.add(m)
    mobs.add(m)

Now you should have a steady stream of mobs coming down the screen, like this:

This is great, but it’s a little boring to just have the mobs all moving straight down. Let’s add a little bit of movement in the x direction as well:

class Mob(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((30, 40))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(WIDTH - self.rect.width)
        self.rect.y = random.randrange(-100, -40)
        self.speedy = random.randrange(1, 8)
        self.speedx = random.randrange(-3, 3)

    def update(self):
        self.rect.x += self.speedx
        self.rect.y += self.speedy
        if self.rect.top > HEIGHT + 10 or self.rect.left < -25 or self.rect.right > WIDTH + 20:
            self.rect.x = random.randrange(WIDTH - self.rect.width)
            self.rect.y = random.randrange(-100, -40)
            self.speedy = random.randrange(1, 8)

Note that we needed to change the if statement that respawns the mob when it goes offscreen. A mob moving diagonally will go off the side long before it goes off the bottom, so this way we are sure to reset it relatively quickly.

Your game should now look like this:

In the next lesson, we’ll learn how to detect when two sprites run into each other (collide) and enable the player to shoot back at the mobs.

Full code for this part

Part 3: Collisions (and Bullets)

Comments