8. Tutorial: Chase game
In this chapter we will build a complete chase game together, step by step. The Python we will use is very simple: just conditionals and loops.
The techniques here should be familiar to you because we used them in Program 4.4, Program 4.5, Program 5.1 and Program 5.2
Now we will show you how to put them all together in one program.
8.1. Moving Actor over a background
We must create two image files for our game. You can use a program such
as Krita 1 to draw them and save them in the mu_code/images
folder (accessible with the images
button in Mu). One is the player,
called player.png
. It should be small, about 64×64 pixels. Ideally
it should have a transparent background.
The other is the background for the game itself. It can look like whatever you want, but it must be the same size as the game window, which will be 600×600 pixels.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | WIDTH = 600
HEIGHT = 600
background = Actor("background")
player = Actor("player")
player.x = 200
player.y = 200
def draw():
screen.clear()
background.draw()
player.draw()
def update():
if keyboard.right:
player.x = player.x + 4
if keyboard.left:
player.x = player.x - 4
if keyboard.down:
player.y = player.y + 4
if keyboard.up:
player.y = player.y - 4
|
Exercise
Run the program and move the Actor around with the keys.
8.2. Screen wrap-around
One problem you will soon find with the program is that you can move off
the edge of the screen and get lost. One way to solve this would be to
stop movement at the screen edges. Instead we going to make the player
teleport to the opposite edge when he leaves the screen. Add this code
to the end of the program, and make sure it is indented so that it
becomes part of the update()
function.
if player.x > WIDTH:
player.x = 0
if player.x < 0:
player.x = WIDTH
if player.y < 0:
player.y = HEIGHT
if player.y > HEIGHT:
player.y = 0
Exercise
Change the code so that the player stops at the edges rather than wraps-around.
8.3. Enemy chases the player
Let’s add an enemy to chase the player. At the top of the program, create a variable to store the enemy Actor:
enemy = Actor("alien")
At the end of the draw()
function (but still indented so part of the
function), draw the enemy. Remember there is only ever one single
draw()
function. No program has two. All drawing goes inside this
function.
Here is the complete function including the new line at the end:
def draw():
screen.clear()
background.draw()
player.draw()
enemy.draw()
At the end of the update()
function (but still indented so part of
the function), add these lines to move the enemy:
if enemy.x < player.x:
enemy.x = enemy.x + 1
if enemy.x > player.x:
enemy.x = enemy.x - 1
if enemy.y < player.y:
enemy.y = enemy.y + 1
if enemy.y > player.y:
enemy.y = enemy.y - 1
if player.colliderect(enemy):
exit()
Exercise
Run the program verify the enemy chases the player.
Exercise
Make the enemy faster so the game is more difficult.
Exercise
Create an image file enemy.png and save it in the images folder. Change the code so it loads “enemy” instead of “alien”.
8.4. Collecting items
Create a small image file coin.png
and save it in the images
folder. It should look like a coin or something else you would like to
collect. We will also need a variable to store the score, i.e. number
of coins collected.
coin = Actor("coin", pos=(300,300))
score = 0
At the end of the draw()
function (but still indented so part of the
function), draw the coin. Remember there is only ever one single
draw()
function. No program has two. All drawing goes inside this
function.
Here is the complete function including the new line at the end:
def draw():
screen.clear()
background.draw()
player.draw()
enemy.draw()
coin.draw()
At the end of the update()
function (but still indented so part of
the function), add these lines to move the coin when it is collected:
if coin.colliderect(player):
coin.x = random.randint(0, WIDTH)
coin.y = random.randint(0, HEIGHT)
score = score + 1
print("Score:", score)
Exercise
Run the program and collect a coin. What happens?
NameError: name 'random' is not defined
This happens because we are using a function randint()
to get a
random number. This function is not build-in to Python; it is part of
the random library. So at the top of the program, add the first line:
import random
Exercise
Run the program again and collect a coin. Does it work now?
No!
UnboundLocalError: local variable 'score' referenced before assignment
You will get an error because score
is a global variable and we are
trying to modify it inside the update()
function. Therefore at the
top of the update()
function, add this line to declare to Python
our intention to modify a global variable:
global score
It must be the first line in the function and it must be indented. The lines surrounding it should look like this:
def update():
global score
if keyboard.right:
Exercise
Run the program again and verify it works!
8.5. Player 2
We can make any game into a two player game. At the top of the program, create a variable to store the Actor for the second player:
player2 = Actor("alien")
At the end of the draw()
function (but still indented so part of the
function), draw the enemy. Here is the complete function with the new
line at the end:
def draw():
screen.clear()
background.draw()
player.draw()
enemy.draw()
coin.draw()
player2.draw()
At the end of the update()
function (but still indented so part of
the function), add these lines to move the second player:
if keyboard.d:
player2.x = player2.x + 4
if keyboard.a:
player2.x = player2.x - 4
if keyboard.s:
player2.y = player2.y + 4
if keyboard.w:
player2.y = player2.y - 4
if player.colliderect(player2):
exit()
Advanced
Create a variable score2 and store the score for player two, i.e. it goes up when he collides with a coin.
8.6. Showing the score on the screen
At the end of the draw()
function (but still indented so part of the
function), draw a title on the screen:
screen.draw.text("My game", (200,0), color='red')
The draw.text()
function is not like print()
- it can only print
strings of text, not numbers. Therefore we must convert the score into a
string. Add these lines to the end of the draw()
function:
score_string = str(score)
screen.draw.text(score_string, (0,0), color='green')
Exercise
Change the colour of the text.
Advanced
Display the word “Score: ” before the score.
Advanced
When the score reaches 10, show a message on the screen to congratulate the player
8.7. Timer
Add a variable at the top of the program (but preferably after any
import
statements) to store the number of seconds of time remaining
in the game:
time = 20
Pygame Zero calls our update()
function many times per second. We
can ask it to tell us how much time has passed by adding a parameter
to the function, delta
. We then subtract this from the remaining
time. Modify update()
so the first lines look like this:
def update(delta):
global score, time
time = time - delta
if time <= 0:
exit()
We can also display the time on the screen. At the end of the draw()
function (but still indented so part of the function) add these lines:
time_string = str(time)
screen.draw.text(time_string, (50,0), color='green')
Exercise
Run the program. Could the displayed time be improved?
We don’t need to see the decimal places in the time. Modify those lines
to use the round()
function, like this:
time_string = str(round(time))
screen.draw.text(time_string, (50,0), color='green')
8.8. Finished game
Here is the finished game with all the changes included:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import random
WIDTH = 600
HEIGHT = 600
background = Actor("background")
player = Actor("player")
player.x = 200
player.y = 200
enemy = Actor("alien")
player2 = Actor("player")
coin = Actor("alien", pos=(300,300))
score = 0
time = 20
def draw():
screen.clear()
background.draw()
player.draw()
enemy.draw()
player2.draw()
coin.draw()
screen.draw.text("My game", (200,0), color='red')
score_string = str(score)
screen.draw.text(score_string, (0,0), color='green')
time_string = str(round(time))
screen.draw.text(time_string, (50,0), color='green')
def update(delta):
global score, time
time = time - delta
if time <= 0:
exit()
if keyboard.right:
player.x = player.x + 4
if keyboard.left:
player.x = player.x - 4
if keyboard.down:
player.y = player.y + 4
if keyboard.up:
player.y = player.y - 4
if player.x > WIDTH:
player.x = 0
if player.x < 0:
player.x = WIDTH
if player.y < 0:
player.y = HEIGHT
if player.y > HEIGHT:
player.y = 0
if enemy.x < player.x:
enemy.x = enemy.x + 1
if enemy.x > player.x:
enemy.x = enemy.x - 1
if enemy.y < player.y:
enemy.y = enemy.y + 1
if enemy.y > player.y:
enemy.y = enemy.y - 1
if player.colliderect(enemy):
exit()
if keyboard.d:
player2.x = player2.x + 4
if keyboard.a:
player2.x = player2.x - 4
if keyboard.s:
player2.y = player2.y + 4
if keyboard.w:
player2.y = player2.y - 4
if player.colliderect(player2):
exit()
if coin.colliderect(player):
coin.x = random.randint(0, WIDTH)
coin.y = random.randint(0, HEIGHT)
score = score + 1
print("Score:", score)
|
8.9. Ideas for extension
There are many things you could add to this game.
Add more enemies.
Give the player three lives.
Add music and sound effects.
Create a powerup that makes the player move faster.
Make the enemies more intelligent.
Allow the player to kill the enemies.