During this tutorial, I will walk you through the creation of a simple game using the PyGame Zero (PgZero) framework. For reference, a completed version of the game can be viewed here.
Everything you need to complete the tutorial should be included here, but if you'd like to learn more about Python (the programming language PyGame Zero uses) or PgZero itself, here are a few resources.
This tutorial has been designed so that you can complete everything online; there's no need to download or install Python or PgZero. To get started, simply use this template on Replit by clicking the green "Use Template" button on the right-hand side.
Once you finish creating the project, click the "Run" button at the top of the window to test the game in its current state. A black window should appear on the right-hand side of the screen.
On the left hand side you'll see a panel that lists all of the files in the project. Most of these are used to setup the Replit environment, so you don't need to worry about them. Just take a look at these two items:
main.py
: This file contains all of the code for the game itself. It's the only file you will need to edit during this tutorial.images/
: This folder contains all of the images you'll need for the project.main.py
Currently, the main.py
file contains three things:
WIDTH
and HEIGHT
variables, which PgZero uses to set the width and height of the game's window (note that this doesn't necessarily equal the size of Replit's output panel, so you may have to resize the panel to see your entire game).draw
function, which PgZero calls once every frame (i.e. the code inside this function will run every frame). We'll put our drawing logic here.update
function, which PgZero also calls once every frame. We'll put all of the logic that updates our game state in here. This function also provides the dt
parameter, which specifies how much time has elapsed since the last frame. We'll use this smooth out our player movement animations.Finally, note that there are (or will be) many words in main.py
that are underlined in red. Normally this would indicate that there is an error, but, because the Replit environment isn't perfectly setup to work with PgZero, it sometimes shows false positives. To see if there are actually errors in your code, pay attention to the console panel on the bottom right-hand side of the window. If there is an error in your code, an error message will be printed there.
We'll start by creating a spaceship sprite (a sprite is image that represents a character or object in a game). To do this with PgZero, we'll use the Actor
class. We'll also use the parameter "spaceship"
, which will tell PgZero to use the spaceship.png
image in the images/
folder.
Add the following code directly below the WIDTH
and HEIGHT
declarations.
ship = Actor("spaceship")
ship.pos = WIDTH / 2, HEIGHT / 2
Note that the second line tells PgZero to place the spaceship in the middle of the game's window.
If you run the game right now, you won't be able to see the spaceship. That's because we still need to tell PgZero to draw it on the screen. To do that, add the following lines to the draw
function.
def draw():
# This tells PgZero to clear the screen each frame.
# If we don't do that, the images will smear
screen.clear()
# This draws the ship on the game's screen
ship.draw()
Make sure to remove the pass
keyword from the draw
function. It's only used when there are no other lines in a function.
Next, we'd like the spaceship to move around the screen when the player presses A,S,W,D or the arrow keys. To achieve this, we'll use a series of if
statements (statements which only run when a condition is true) to change the ship's x or y position when the correct keys are currently being held down. Since we want this code to update the game state (i.e. the spaceship's position), we'll put this code in the update
function.
To detect when a key is pressed, PgZero provides keyboard.<key name>
, a value which is true whenever the corresponding key is pressed.
When a key is pressed, we want to move the spaceship at a certain speed. We might want to change this speed in the future, so in order to make our code easier to edit we'll make a spaceship speed variable. This way, if we want to change the value, we'll only have to change one part of the code.
Add the following code directly below the ship's pos declaration.
ship_speed = 80
Then, to actually make the spaceship move, add the following code to the update
function. Note that we're using ship_speed * dt
for our actual speed so that it remains consistent even if the frame rate changes.
def update(dt):
# Ship movement
if keyboard.a or keyboard.left:
ship.x -= ship_speed * dt
if keyboard.d or keyboard.right:
ship.x += ship_speed * dt
if keyboard.w or keyboard.up:
ship.y -= ship_speed * dt
if keyboard.s or keyboard.down:
ship.y += ship_speed * dt
Make sure to remove the pass
keyword from the function before adding all of this code.
Finally, run the game again and try to use the arrow keys to move the ship around the screen. To actually see the output, you may have to click the "Try again" button (if there was a server error) or refresh the entire page.
Next, we'd like our spaceship to shoot bullets. Since there will be multiple bullets on the screen at the same time (and we don't know how many), we'll start by creating an array (a list of things in Python) to store all of our bullets.
Put the following code below the ship_speed
declaration.
bullets = []
Now we'll write the bullet creation logic. We'd like a single bullet to be created each time the player presses the spacebar. This means that we can't put this logic in the update
function. If we did, a bullet would be created every frame. Instead we'll use the on_key_down
function, which PgZero calls once every time a key is pressed. To make sure the spacebar is the key being pressed, we'll compare the key
parameter of this function with the keys.SPACE
constant, which represents the spacebar. Remember that keyboard.space
is its own boolean (true or false) value, so we can't use it in this context. Once the condition is met, we'll use the Actor
class again to create a sprite for the bullet and then add the bullet to our array of bullets.
Put the following code below the entire update
function.
def on_key_down(key):
if key == keys.SPACE:
bullet = Actor("bullet", center=ship.center)
bullets.append(bullet)
We'll also need to add the code that draws the bullets. Add the following code to the draw
function, but be sure not to copy the ...
or any the code preceding it. That's just there to help you figure out where to put the new code.
def draw():
...
# Loop through the list of bullets and draw each one
for b in bullets:
b.draw()
Now, if you run the game again, you'll notice that a bullet is created everytime the spacebar is pressed. But the bullets don't move!
To make them move, we'll add another speed variable and the make changes to the update
function.
First, add the following line directly below the ship_speed
declaration.
bullet_speed = 200
Then add the following code to the update
function.
def update(dt):
...
# Loop through the bullets and update their state
for b in bullets:
# Bullet movement
b.y -= bullet_speed * dt
# Remove the bullets that leave the screen
if b.y < -5:
bullets.remove(b)
Next, we'll add the code that actually creates each asteroid. We don't want to create one every frame, so we can't put this code in the update
function. Instead, we'll write a new function and then tell PgZero to run it every three seconds.
We'd also like to place each asteroid in a random spot, so we'll need the randint
function from the random
library. Add the following line at the top of the file.
from random import randint
Then put the following code below the entire update
function.
def spawn_asteroid():
asteroids.append(Actor("asteroid", center=(randint(0, WIDTH), -5)))
Next, place the following code below the spawn_asteroid
function.
clock.schedule_interval(spawn_asteroid, 3)
This tells PgZero to run the spawn_asteroid
every three seconds. Make sure that this line is NOT indented so that it is not included in the spawn_asteroid
function.
Now we're ready to start drawing asteroids on the screen. To do this, add the following code to the draw
function.
def draw():
...
# Loop through the asteroids and draw them
for a in asteroids:
a.draw()
To make the asteroids move down the screen, first add the following line right after the bullet_speed
declaration.
asteroid_speed = 40
Then add the following code to the update
function.
def update(dt):
...
# Move and potentially remove asteroids
for a in asteroids:
a.y += asteroid_speed * dt
if a.y > HEIGHT:
asteroids.remove(a)
Finally, we would like the bullets to destroy the asteroids when they hit them. To do this, we will need to detect when a collision happens between a bullet sprite and an asteroid sprite. PgZero provides several functions for detecting collisions:
Rect.collidepoint(point)
: this returns true if the rectangle is colliding with the given pointRect.colliderect(Rect)
: this returns true if the rectangle is colliding with another rectangleRect.collidelist([])
:Thankfully, with PgZero, we can substitute Actor
s for Rect
s, so our collition code will be fairly simple.
Add the following code inside the bullets for
loop that is inside the update
function. Take extra care to get the indentation correct, because it tells Python which block of code each line belongs to.
def update(dt):
...
for b in bullets:
...
asteroid_index = b.collidelist(asteroids)
if asteroid_index > -1:
a = asteroids[asteroid_index]
asteroids.remove(a)
bullets.remove(b)
At this point you should have a simple space-shooter game. However, a lot of improvements could be made.
These are just a few ideas. I'd encourage you to keep playing around with this if you want to get even more comfortable with the Python programming language and PyGame Zero.