Pygame Shmup Part 1: Player Sprite and Controls

Tags: python tutorial gamedev pygame

Let’s make our first game! 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:

Getting started

If you haven’t already, please go back and complete the first lesson on Getting Started with Pygame. We’ll be using the pygame template.py program that we built in that lesson as the starting point for this one.

In this series we’ll be making a “Shmup” or “Shoot ‘em up” style game. In our case, we’ll be the pilot of a small spaceship trying to stay alive while meteors and other objects come flying at us.

To begin, save your pygame template.py file with a new name. This way you will still have the template when you need it for another new game. I chose to save mine as shmup.py.

First, let’s modify the game settings to some good values for the game we’re making:

WIDTH = 480
HEIGHT = 600
FPS = 60

This is going to be a “portrait mode” game, meaning the height of the window is greater than the width. It’s also an action game, so we want to make sure our FPS is high enough that fast moving sprites will look like they’re moving smoothly. 60 is a good value for this.

Player sprite

The first thing we’ll add, is a sprite to represent the player. Eventually, this will be a spaceship, but when you’re first starting, it’s simpler to ignore the graphics for a bit and just use plain rectangles for all your sprites. Just focus on getting the sprite on the screen and moving the way you want it to, then you can replace the rectangle with your nice-looking art.

Here is the start for our player sprite:

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.centerx = WIDTH / 2
        self.rect.bottom = HEIGHT - 10
        self.speedx = 0

We’ve chosen a size of 50x40 pixels for our player, and we’re locating its rect at the bottom center of the screen. We’ve also made a speedx property that will keep track of how fast the player is moving in the x direction (side-to-side). If you’re unsure what any of this is doing, please see the earlier lesson on Working with Sprites.

For the update() method of our Sprite, which is the code that will happen every frame of the game, we just want to move the sprite at whatever its speed is:

    def update(self):
        self.rect.x += self.speedx

Now, we can just spawn our sprite to make sure we see it on the screen:

all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

Don’t forget that every sprite you create must be added to the all_sprites group so that it will be updated and drawn on the screen.

Movement / Controls

This is going to be a keyboard controlled game, so we want the player to move when the Left or Right arrow keys are pressed (you can also use a and d if you prefer).

We have a choice when we’re thinking about using key presses in our game, which means we are talking about Events:

    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player.speedx = -8
            if event.key == pygame.K_RIGHT:
                player.speedx = 8

The problem with this method is that while pressing the key will make the Player move, there is no way to stop! We would also need to add two KEYUP events that set the speed back to 0.

def update(self):
        self.speedx = 0
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT]:
            self.speedx = -8
        if keystate[pygame.K_RIGHT]:
            self.speedx = 8
        self.rect.x += self.speedx

This sets the speedx to 0 every frame, and then checks to see if a key is down. The pygame.key.get_pressed() results in a dictionary of every key on the keyboard, with a value of True or False indicating whether the key is currently down or up. If either of the keys we want are down, we set the speed accordingly.

Staying on the screen

Finally, we need to make sure that our sprite doesn’t go off the screen. We’re going to add the following to the Player update():

        if self.rect.right > WIDTH:
            self.rect.right = WIDTH
        if self.rect.left < 0:
            self.rect.left = 0

Now if the rect ever tries to move to a position that is past the left or right edges of the screen, it will stop. Another option would be to wrap around the screen - teleporting the sprite from one side to the other when it hits the edge - but for this game, stopping on the edges feels more appropriate.

Wrapping up

Here is the full code for this step:

# KidsCanCode - Game Development with Pygame video series
# Shmup game - part 1
# Video link: https://www.youtube.com/watch?v=nGufy7weyGY
# Player sprite and movement
import pygame
import random

WIDTH = 480
HEIGHT = 600
FPS = 60

# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)

# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Shmup!")
clock = pygame.time.Clock()

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.centerx = WIDTH / 2
        self.rect.bottom = HEIGHT - 10
        self.speedx = 0

    def update(self):
        self.speedx = 0
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT]:
            self.speedx = -8
        if keystate[pygame.K_RIGHT]:
            self.speedx = 8
        self.rect.x += self.speedx
        if self.rect.right > WIDTH:
            self.rect.right = WIDTH
        if self.rect.left < 0:
            self.rect.left = 0

all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

# Game loop
running = True
while running:
    # keep loop running at the right speed
    clock.tick(FPS)
    # Process input (events)
    for event in pygame.event.get():
        # check for closing window
        if event.type == pygame.QUIT:
            running = False

    # Update
    all_sprites.update()

    # Draw / render
    screen.fill(BLACK)
    all_sprites.draw(screen)
    # *after* drawing everything, flip the display
    pygame.display.flip()

pygame.quit()

In the next lesson, we’ll add some enemy sprites for our player to dodge.

Part 2: Enemy Sprites

Comments