Pygame Shmup Part 1: Player Sprite and Controls
Wed, Aug 17, 2016Let’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:
- Option 1: In our event queue we could define two events (one for each key) and each one changes the player’s speed accordingly:
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
.
- Option 2: We tell the sprite to always set its speed to
0
, unless theLEFT
orRIGHT
key is down. This is will result in smoother movement, as well as being a bit simpler to code:
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.