Introduction
In this lesson, you will be creating a 2D scroller game where the player
controls a space ship, and has to shoot as many aliens as possible before they
run out of lives.
You will be using python
and pygame
to create this game.
Task
Create a new folder where we will store everything for the game. Call it
something like space_game
.
Download Files
Here are the files that you need to download to use in your game.
Attribution and copyright for these images can be found on the lesson
details page.
Task
Download each of these files into the new folder you just created, but
don't change their names.
Understanding Screens
Throughout the development of the game, we will be interacting with the
computer screen. Computer screens are made up of something called pixels,
a pixel is a very very tiny dot on the screen that can be many different
colours. Pixels of a computer screen are arranged into a rectangular grid, and
they work the exact same way as a television. A typical television will be
1,920
pixels wide and 1,080
pixels tall, which means that it has
2,073,600
pixels filling the whole screen.
How movement on a screen work (frames)
To make it seem like things move on a computer screen or a television, what
happens is that the image changes many times a second, to trick our eyes into
thinking that things are moving. The number of times the picture changes every
second is called the framerate.
When we create our game, we will have a game window, which will be a
rectangular area, a certain number of pixels wide and high, which we will draw
to. When we want to show something moving in the window, we will re-draw the
entire window, and move the objects slightly, a certain number of times per
second.
Getting Started
Task
Create a new python file called game.py
in your python text
editor, enter the following code, and save the file in the folder
you just created.
import pygame
import sys
pygame.init()
window = pygame.display.set_mode((800, 500))
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
clock.tick(50)
Note
Pay very close attention to the amount of space before the code on each
line. This is called indentation.
Run Your Code
Try running your code now. You do this by typing python
game.py
from inside your terminal. (Make sure that your terminal is
in the same folder that all your game files are in).
If everything is working, a new black window should have appeared with nothing
inside. This is going to be our game screen, and we will start adding things to
it.
Remember
If you have run into a problem, ask your teacher or a helper before
continuing.
Explaining The Starting Code
Now that we have the starting code for our game, lets go through and see what
each bit does.
These first two lines tell python that we want to use some extra code from
outside of our game.py
file. In particular we want to use pygame
and sys
at the moment. These are called modules
.
We are going to use the pygame
module a lot in this project.
This line tells pygame
to make sure that everything is ready for us to use.
window = pygame.display.set_mode((800, 500))
There are a few important things going on on this line. One of the first things
you should notice is that there is an equals =
sign. What an equals sign
allows us to to is create variables.
A variable is like a box in our program that we can use to store stuff in,
and every variable has a name. If we write this code:
… what we have done is create a new variable, a new box, and given it the
name my_variable
. We’ve also put the number 4
inside it.
If we write this code:
another_variable = 20
another_variable = 21
… then we have created a variable called another_variable
, given it the
value 20
, and then changed it straight away by giving it the value 21
.
Let’s have a look at the code we’ve already written again:
window = pygame.display.set_mode((800, 500))
The part of the code to the right of the equals sign,
pygame.display.set_mode((800, 500))
, basically tells pygame
that we want
a window on the computer screen that is 800
pixels wide, and 500
pixels
tall, and this is what it returns. This means that after this line of code is
run, we have a variable called window
(remember a variable is a box), and
inside that variable is a window that we can manipulate, by drawing things on
it.
The next line is a little bit simpler:
clock = pygame.time.Clock()
What this does is create a variable called clock
, and make the value of that
variable a new Clock
. A pygame
clock is important, because it allows us to
control the frame rate (remember, the frame rate is how many times the screen
changes per second). You will see how we use the clock very shortly.
The next part of the code is where the magic happens:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
clock.tick(50)
This is what we call our event loop. The first line while True
, means “do
this forever”. Every line under this line has some extra space at the start of
it, this is what’s called indentation. This is important because it tells
python that these lines of code are inside the loop, which means every time
the loop runs, it will run all the code that has the correct amount of space at
the start.
We are going to use our event loop at the heart of our game, and we will run
the loop once for each time we want to create the next frame of our window.
Every time we want to create the next frame, we will:
- Check if the user has interacted with the window at all (for example pressed
an arrow key, or closed the window).
- Update information about the objects on the screen.
- Draw a new frame from scratch.
- Display that frame in our window.
Finally, let’s address the last bits of the code.
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
We’ll skip over an explanation for these three lines of code for now, but they
will be explained later on. For now, all you need to know is that it checks if
the user has closed the window, and if the window has been closed we close the
whole program.
Now this is a clever bit of code. Because computers are so fast, if we let the
event loop run without any limitation, it would run many many thousands of
times faster than we would like, because the number of times it runs per second
will determine what our framerate is. This means that things moving on the
screen will probably be moving much faster than we would like.
One of the first things you should notice about this line is that it starts
with the same name as the variable clock
that we created earlier, that is
because it is actually using the value stored inside that variable. We created
a pygame
clock, and saved it in the variable clock
, and now we have used
it. This line of code makes our program pause (basically slows it down), so it
only runs as fast as we want it to. Because we gave it the value 50
, it will
make sure that our loop does not run more than 50
times per second, so that
our frame rate will also not be faster than 50
times per second.
Later on, when we have things moving on screen, you will have the opportunity
to experiment with the frame rate, and see what the effect of it is on your
game.
Task: Experiment!
Change the numbers for the line that tells pygame
the width
and height, and run your code each time you change it. Do this a few times.
Remember to change the numbers back to what they were before you continue.
Adding a Space Ship
Now let’s add our first image to the game.
Task:
Add these lines of code to your program above the while loop:
background = (0, 0, 0)
ship_image = pygame.image.load("ship_normal.png")
And add these lines of code above the clock tick, indented by
four spaces.
window.fill(background)
window.blit(ship_image, (0, 0))
pygame.display.flip()
Your code should now look like this:
import pygame
import sys
pygame.init()
window = pygame.display.set_mode((800, 500))
clock = pygame.time.Clock()
background = (0, 0, 0)
ship_image = pygame.image.load("ship_normal.png")
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
window.fill(background)
window.blit(ship_image, (0, 0))
pygame.display.flip()
clock.tick(50)
Note
Python does not care how many blank lines there are between lines of code,
but it can make it easier to read and understand what's going on.
Run Your Code
Try running your code now. If everything is working, you should now see
a black window with a space ship in the top left, like this:
Lets have a look at what the code that we have added is doing:
background = (0, 0, 0)
ship_image = pygame.image.load("ship_normal.png")
The first line is creating a variable called background
. The value of this
variable is 3 numbers grouped together, into something called a tuple. At
the moment, this variable is used in one place, on the line that says
window.fill(background)
, which we will explain in a bit, and we will explain
what those numbers mean.
The second line is creating a variable called ship_image
, and loading an
image file (specifically the one stored as ship_normal.png
), and storing the
image inside that variable.
window.fill(background)
window.blit(ship_image, (0, 0))
pygame.display.flip()
These lines of code are all making changes to what we can see in the window.
And because they are all indented by 4
spaces, they are inside the
while
loop (our event loop), which means they are run once every frame.
The first line fills in the whole window with a specific colour, in this case,
the colour stored in the variable background
, and that is what the tuple
is. The tuple (0, 0, 0)
represents the colour black, but there are over
16 million colours you can choose from!
The way in which we describe a colour to a computer is with what’s called RGB
(which stands for Red, Green and Blue). This is because each pixel
(in most computer screens) is made up of three smaller parts, each of which
emits a different colour light. One part emits red light, one part emits
green light, and another part emits blue light. This allows us to
display a wide range of colours for each pixel on a screen.
The tuple (0, 0, 0)
means we want 0
red, 0
green and 0
blue, which is
how we get black, by displaying no light at all! Each number in a tuple
which describes a colour has to be between 0
and 255
, it can’t be higher
than 255
, and it can’t be lower than 0
. For example, if we wanted to
display as much red as we can, but no green and blue, the colour would be
(255, 0, 0)
.
Task: Experiment!
Change the numbers inside the tuple that we set the
variable background
to, and run your code each
time. See what happens if all the numbers are really big, and
what happens if they are really small.
To really experiment, try adding numbers that are less
than 0
(eg: -10
), or numbers that are
bigger than 255
. If you do this, you should see that your
game CRASHES!.
It might say something like this:
Traceback (most recent call last):
File "game.py", line 19, in <module>
window.fill(black)
TypeError: invalid color argument
This means there is a bug in your program, and python is
trying to tell you where the bug is so that you can fix it. When you
write software and games, you will come accross lots of bugs that you
will need to fix. This is a normal part of learning to program. Even the
best programmers make mistakes and need to fix bugs they created on a
daily basis.
REMEMBER: Don't forget to change the colour back to
black (or to a colour close to black) before continuing.
Lets get back to the code now:
window.fill(background)
window.blit(ship_image, (0, 0))
pygame.display.flip()
The second line of code copies the image that we have loaded and stored in the
variable ship_image
, and places it inside the window. You can see that when
we run the code, the ship is places in the top left hand corner of the screen.
The two numbers in brackets (0, 0)
is what is telling pygame
to do this.
This is another tuple, and this tuple is used to represent
co-ordinates. Specifically, these co-ordinates are the x
and y
positions of where we want the image to be in our window.
- If we increase our
x
value, then the image would be more to the
right.
- If we increase our
y
value, then the image would be further down
the screen.
When x
is 0
and y
is 0
, then we mean the pixel that is furthest to the
top and furthest to the left.
Here’s a diagram to explain:
This is a bit different to what you might be used to with drawing graphs at
school, where the x-axix is the same, but the y-axis goes up instead of
down. Unfortunately this is how pygame
, and a lot of other computer graphics
work, and you will need to remember this difference.
Task: Experiment!
Try changing the numbers for the line of code that copies the space ship
to the window, and run your code. For example, changing
it to (300, 100)
results in this:
The ship has moved 300
pixels right, and 100
pixels down. This is because it now has an x
value of
300
and a y
value of 100
.
If we instead use the co-ordinates (-50, 50)
, we get this:
You can see it has moved off screen.
Lets look at the final line of code we added:
This line just tells pygame
that, now that we have made changes to the
window, we would like it to be updated. The reason it doesn’t update the window
automatically is because if we are doing a lot of changes, and if it takes a
long time, the user would see lots of un finished frames, when we are still
making changes to the window.
By requiring us to tell it when we have finished changing the window, pygame
can make sure that it updates the screen really quickly, all at once, so the
user never sees half-finished frames.
Going over All the Code
Lets go over the whole program, and see if we can understand how it all works
together.
import pygame
import sys
pygame.init()
window = pygame.display.set_mode((800, 500))
clock = pygame.time.Clock()
background = (0, 0, 0)
ship_image = pygame.image.load("ship_normal.png")
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
window.fill(background)
window.blit(ship_image, (0, 0))
pygame.display.flip()
clock.tick(50)
What the code does:
Try and follow along with the code above so that you can make sure that you
understand what each line is doing.
- We tell python that we want to use the
pygame
and sys
modules.
- We tell
pygame
to set up.
- We create a new variable called
window
, and store a window object in there
that we can manipulate.
- We create a new variable called
clock
, and store a pygame
clock in there
so that we can control the framerate of the game.
- We create a new variable called
background
, and store a tuple in there.
We are going to use this tuple as a colour.
- We create a new variable called
ship_image
, and store the image of the
space ship in it.
- We start a loop, that repeats forever, and will run once for every frame.
Inside the loop:
- We check if the user has closed the window, if the window is closed, we
stop the program.
- We fill the whole window with the colour stored in the variable
background
.
- We copy the space ship image stored in
ship_image
to the window, at the
top left of the screen.
- We tell
pygame
that we have finished making changes to the window, and it
should update the screen.
- We make the loop pause for a short period of time so that our frame rate is
not too high.
Making the Space Ship Move
We are now going to use keyboard input to move the space ship around the
screen.
Task:
Firstly add these two lines above the loop:
ship_x = 0
ship_y = 0
We are going to use these two variables to keep track of the space ship’s
position: we will change them when the player presses the right keys, and use
their values to work out where to copy the space ship image to on the window.
Task:
Underneath the code that checks if the user has closed the window, add
this code:
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.K_UP]:
ship_y = ship_y - 10
if pressed_keys[pygame.K_DOWN]:
ship_y = ship_y + 10
if pressed_keys[pygame.K_LEFT]:
ship_x = ship_x - 10
if pressed_keys[pygame.K_RIGHT]:
ship_x = ship_x + 10
Note: make sure you indent it so that it is inside the
loop!
This code that you have just added is what will change the position of the
space ship when you press certain keys on the keyboard.
pressed_keys = pygame.key.get_pressed()
This line creates a variable called pressed_keys
, which stores information on
which keys are currently being pressed by the player. Every time we run the
loop, we want to check if the user is currently pressing a particular key, and
if they are, then we will act accordingly.
The rest of the code uses things called if statements. If statements make
sure that certain parts of the code only run in certain situations.
Lets take a look at the first if statement:
if pressed_keys[pygame.K_UP]:
ship_y = ship_y - 10
Firstly, it uses the variable pressed_keys
to check if the the user is
currently pressing the up key. If they are, then it runs the code which is
indented underneath it.
The code which is indented changes the value of the variable ship_y
to
ship_y - 10
, which means that ship_y
will have a value of 1
0 less than it
had before: it will have been decreased.
The other 3 if statements work in the same way, increasing or
decreasing ship_x
and ship_y
.
Task:
And finally, we want to actually use the variables ship_x
and ship_y
in the part of our code that copies the space
ship image to the window. SO change this line of code:
window.blit(ship_image, (0, 0))
To this:
window.blit(ship_image, (ship_x, ship_y))
Run Your Code
Try running your code now. If everything is working, you should be able
to press the arrow keys on your keyboard to move the space ship in the
correct direction for each key.
You will notice that it is very easy to make the space ship go off the
edge of the window, this is probably something we want to fix later
on…
Task: Experiment!
What happens if you press multiple keys at the same time? What about if
you press up and down at the same time?
Task: Experiment!
Change the amount that we modify ship_x
and
ship_y
by from 10
to something else. What
happens if you have different numbers for different directions? Don't
forget to run your code to find out!
Task: Experiment!
Now that we have movement in our game, try changing the
framerate of our game, and see what effect that has on
our game.
Remember: This is the code that looks like
clock.tick(50)
.
Stop the Space Ship Going Out the Window
We are going to stop the space ship going out the window by being careful about
what values we give to ship_x
and ship_y
.
Task:
After the code that changes the ship_x
and
ship_y
variables, add this:
# Stop ship going out of bounds
if ship_y < 0:
ship_y = 0
if ship_y > window.get_height() - ship_image.get_height():
ship_y = window.get_height() - ship_image.get_height()
if ship_x < 0:
ship_x = 0
if ship_x > window.get_width() - ship_image.get_width():
ship_x = window.get_width() - ship_image.get_width()
Note: make sure you indent it so that it is inside the
loop!
The line beginning with #
is called a comment. We’ve put this here to help us
explain what the code does, and we can write anything after the #
symbol.
Making the Ship File Bullets
Task:
Underneath the code that loads the space ship image, write this code to
load an image of a bullet.
bullet_image = pygame.image.load("bullet.png")
And under this, write the following lines of code.
class Sprite:
pass
def display_sprite(sprite):
window.blit(sprite.image, (sprite.x, sprite.y))
This small bit of code will allow us to create "sprite"
objects, where we will store the image and location of something we want
to draw on screen. It also allows us to draw the sprites much more
easily.
Under the code that sets the ship's position to (0, 0), write this line
of code:
bullets = []
This is where we are going to store a list of all the bullet sprites
(because we can have lots of them at the same time!)
Under that, write this:
def fire_bullet():
bullet = Sprite()
bullet.x = ship_x + 130
bullet.y = ship_y + 100
bullet.image = bullet_image
bullets.append(bullet)
This is a function which handles creating the bullets for us. We can put
fire_bullet()
anywhere we want to trigger a bullet being
fired.
Under the code that changes the position of the ship, write this:
if pressed_keys[pygame.K_SPACE]:
fire_bullet()
And under the code that draws the ship on the window, write this:
for bullet in bullets:
display_sprite(bullet)
Run Your Code
Try running your code now. If everything is working, you should be able
to press the space bar and lots of bullets appear on the screen, perhaps
too many. Also, out bullets currently stay still, we probably want them
to move on the screen!
Our current code will fire a bullet for every frame that the space bar is held
down on, let’s change it so that it is only once every time we press the space
bar.
Task:
Remove the if
statement we created in the last task that
used fire_bullet()
when it detected the space bar was
currently being pressed.
Under the code that calls sys.exit()
, add these lines,
making sure they are indented so that they are inside the for loop.
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
fire_bullet()
Now lets make the bullets move accross the screen
Task:
Add this code under the code that changes the ship's position.
for bullet in bullets:
bullet.x = bullet.x + 13
bullets = [bullet for bullet in bullets if bullet.x < window.get_width()])
Run Your Code
Try running your code now. If everything is working, a much smaller
amount of bullets should be appearing on the screen, and they should also
fly towards the right. It should look like the image below.
Make The Ship Tilt
Images have been included to make the ship tilt to indicate it is rising or
falling. Let’s implement this.
Firstly lets convert the ship into a sprite, like each of the bullets are.
Task:
Replace this code:
ship_x = 0
ship_y = 0
With this:
ship = Sprite()
ship.x = 0
ship.y = 0
ship.image = ship_image
And replace this line (the line that draws the ship):
window.blit(ship_image, (ship_x, ship_y))
With this:
display_sprite(ship)
And finally, replace all instances of ship_x
and
ship_y
with ship.x
and ship.y
respectively, across the entire file.
Run Your Code
Try running your code now. Everything should be working exactly as,
make sure you fix any errors before continuing.
Now we only need to add a few lines to make the ship tilt.
Task:
Under the code that loads the ship image as ship_image
, we
will load the other two images, so write this code there:
ship_image_up = pygame.image.load("ship_up.png")
ship_image_down = pygame.image.load("ship_down.png")
We are going to change the ship image in the same part of the code that
changes its position, before the line of code that sets the
pressed_keys
variable, write this:
ship.image = ship_image
Inside the if statement that checks if K_UP
is pressed, write
this:
ship.image = ship_image_up
And inside the if statement that checks if K_DOWN
is pressed,
write this:
ship.image = ship_image_down
Run Your Code
Try running your code now. Now when the ship moves up and down, it should
tilt, like in the image below.
Adding Aliens
Lets add some aliens that appear on the right hand side of the screen and move
towards the space ship.
Task:
First we need to load an alien image to use, after the code that loads
the bullet image, write this line:
alien_image = pygame.image.load("alien_1.png")
We need to create a list to store the alien sprites in, under the line
of code where we create the variable bullets
, write this:
aliens = []
We want the aliens to appear with some time delay between each one, so we
will create a variable to count how many frames we have left before we
will display another alien. So add this code after the last line you
wrote:
frames_until_next_alien = 50
Under the fire_bullet
function, write this code:
def add_alien():
alien = Sprite()
alien.x = window.get_width()
alien.y = window.get_height() / 2
alien.image = alien_image
aliens.append(alien)
This is a function that we can use at any point in our code to create an
alien.
Before the line of code that fills the background
(window.fill(background)
), write this:
frames_until_next_alien = frames_until_next_alien - 1
if frames_until_next_alien <= 0:
frames_until_next_alien = 50
add_alien()
for alien in aliens:
alien.x = alien.x - 3
aliens = [alien for alien in aliens if alien.x > - alien_image.get_width()]
This code calls the function add_alien()
once every 50
frames, and also deleted aliens once they go off the screen (so we can
forget about them)
And after the code that displays the bullets, write this to display the
aliens on screen.
for alien in aliens:
display_sprite(alien)
Run Your Code
Try running your code now. If everything is working, you should now see
aliens starting to appear from the right of the screen, all in a line.
Adding A Little Randomness
Currently the aliens all appear at the same delay, and in the same position,
this is a little boring and predictable, lets add some randomness.
Task:
Firsly lets make the delay between new aliens random, underneath the line
import sys
, right at the top, write this:
import random
Next, on the line frames_until_next_alien = 50
, which should
be near the bottom of your file, replace the number 50
with
random.randrange(30, 100)
. This will make the gap between
aliens appearing be anything between 30 to 100 frames.
Run Your Code
Try running your code now. You should see the gaps between aliens become
a bit random, like this:
Task:
Now lets make them appear at different heights on the screen, in the
add_alien()
function, where we set the y
of the
alien sprite, instead of window.get_height() / 2
, give it
this value:
random.randrange(100, window.get_height() - 100)
Run Your Code
Try running your code now. You should now also see that aliens have
random y positions like this:
Adding Stars to the Background
We are going to add stars to the screen, and they are going to behave in a very
similar way to the aliens.
We will not load an image for the stars, instead we will create small white
dots using code.
Task:
To begin with, we need to store the stars we have on the screen in a
variable, so under the code where we create the
aliens
variable, write this:
stars = []
Like with the aliens, we will keep track of when we want our next star to
appear by counting down frames, so under the code where we create
the frames_until_next_alien
variable, write this:
frames_until_next_star = 0
Like both the aliens and bullets, we are going to create a function that
creates a single star. Under the add_alien
function, add the following code.
Remember that everything that is indented under the
function definition is part of the function, so you need to add the code
under that.
def add_star():
star = Sprite()
star.x = window.get_width()
star.y = random.randrange(10, window.get_height() - 10)
star.image = pygame.Surface((2, 2))
star.image.fill((255, 255, 255))
stars.append(star)
Next, we need to actually call the function add_star
at
regular intervals, move the stars' x positions in each frame, and remove
the stars when they go off the screen, so before the line saying
window.fill(background)
, add this code:
frames_until_next_star = frames_until_next_star - 1
if frames_until_next_star <= 0:
frames_until_next_star = random.randrange(10, 30)
add_star()
for star in stars:
star.x = star.x - 2
stars = [star for star in stars if star.x > - 10]
Finally, we need to actually paint the stars, so this time, under
the line saying window.fill(background)
, write
this:
for star in stars:
display_sprite(star)
Run Your Code
Try running your code now. You should now see stars start to appear.
Task:
To make the stars look a little more interesting, we can give them
different sizes randomly. In the add_star
function, replace the line
star.image = pygame.Surface((2, 2))
with these two lines:
star_size = random.randrange(1, 4)
star.image = pygame.Surface((star_size, star_size))
This makes the size of any star anything from 1x1 pixels to 4x4 pixels
square.
Run Your Code
Try running your code now. You should now see stars have varying sizes.
Making The Bullets Destroy Aliens
Lets make the bullets destroy the aliens on the screen.
Task:
In the fire_bullet
function, before the
line bullets.append(bullet)
, add this:
bullet.used = False
In the add_alien
function, before the line
aliens.append(alien)
, add this:
alien.hit = False
after the add_star
function, add this
function:
def get_sprite_rectangle(sprite):
return sprite.image.get_rect().move(sprite.x, sprite.y)
This function takes a sprite as an argument, and returns a rectangle
representing its position on the screen, which we will use to check if
two sprites have collided with one another.
Above the window.fill(background)
line,
add this bit of code:
for alien in aliens:
alien_rect = get_sprite_rectangle(alien)
for bullet in bullets:
if alien_rect.colliderect(get_sprite_rectangle(bullet)):
alien.hit = True
bullet.used = True
continue
Replace the line
aliens = [alien for alien in aliens if alien.x > - alien_image.get_width()]
with this:
aliens = [alien for alien in aliens if alien.x > - alien_image.get_width() and not alien.hit]
And replace the line bullets = [bullet for bullet in bullets if bullet.x < window.get_width()])
bullets = [bullet for bullet in bullets if bullet.x < window.get_width() and not bullet.used]
Run Your Code
Try running your code now. After shooting bullets and hitting aliens,
both the aliens and the bullets should disappear.
Adding a Score
We will want to keep track of the players score throughout the game, and
eventually keep track of a high score. So lets add a score.
Task:
Firstly, above the line background = (0, 0, 0)
(right at the top of your file), write this code which creates
a font we will use for our score text, and a foreground variable which
will be our text colour.
font = pygame.font.Font(None, 24)
foreground = (200, 200, 200)
Next, above the line bullets = []
create a
new variable called score
like this:
score = 0
This variable will keep track of the user's score, and we will now
increase it when aliens are hit.
Find the for
loop that we added in the last task, and
under the line bullet.used = True
, write
the following:
Make sure that you indent it the same as the line above!
score = score + 10
What this does is add 10
to the user's score every time an
alien is hit with a bullet!
Finally, we need to actually write the score on the screen with some
text, so before the line
pygame.display.flip()
, write this:
score_text = font.render("SCORE: " + str(score), 1, foreground)
score_text_pos = score_text.get_rect()
score_text_pos.right = window.get_width() - 10
score_text_pos.top = 10
window.blit(score_text, score_text_pos)
Run Your Code
Try running your code now. You should now see that you have a score in
the top right hand corner, and when you shoot aliens, your score should
go up by 10
each time.
Exploding Aliens
Lets add a little animation to the aliens after you shoot them.
Task:
Under the line of code where we load the alien image,
write this:
alien_dead_image = pygame.image.load("alien_1_dead.png")
This is the image we will display and fade out when an alien is hit.
Inside the add_alien
function, after the line
alien.hit = False
, write this:
alien.alpha = 255
This will hold the number representing how opaque / transparent we want
the alien to be, so we can have a fade out animation.
Inside the for loop that changes each alien's x position on each
frame, under the line that changes the x position, write this:
if alien.hit:
alien.alpha = max(0, alien.alpha - 10)
This will change the value we will use for the transparency on each frame
for any alien that is hit.
Below that, on the line that looks like:
aliens = [alien for alien ...]
replace the code
alien.hit
with:
(alien.hit and alien.alpha == 0)
So that aliens are only deleted after they have faded out, and not as
soon as they are hit.
Inside the for
loop which detects when aliens have
been hit by bullets, before the line where you write
alien_rect = get_sprite_rectangle(alien)
, write
this:
if alien.hit:
continue
To prevent aliens that have been hit from being hit multiple times while
they are fading out, before they have been deleted.
In the same loop, after the line
alien.hit = True
, write these two lines to correct
the position of the alien after the image is swapped:
alien.x = alien.x - 6
alien.y = alien.y - 6
And the final piece of the puzzle is to draw the alien with transparency
if it is hit, and to use a different image. So in the for loop
that draws the aliens on the screen, above the line
display_sprite(alien)
, write:
if alien.hit:
tmp = pygame.Surface( alien_dead_image.get_size(), pygame.SRCALPHA, 32)
tmp.fill( (255, 255, 255, alien.alpha) )
tmp.blit(alien_dead_image, (0,0), alien_dead_image.get_rect(), pygame.BLEND_RGBA_MULT)
alien.image = tmp
Run Your Code
Try running your code now. After shooting bullets and hitting aliens,
their image should change, then fade out.
Adding Lives
We don’t want the player to be invincible, so lets add lives, and reduce the
player’s life when the ship gets hit by an alien.
Task:
Firstly, below the line where we create the score variable,
score = 0
, write this:
lives = 3
This will keep track of the number of lives we have given to the player.
Under the line ship.y = 0
, write this:
ship.red = 0
We will use this value to make the ship flash red when a life is lost, when
the ship is completely red, this value will be 255
, and when
it is normal it will be 0
. It will gradually change from
255
to 0
to create a fade effect after being hit.
Above the for loop that detects when aliens are hit by bullets (it
starts with for alien in aliens:
, write this:
ship.red = max(0, ship.red - 10)
ship_rect = get_sprite_rectangle(ship)
This decreases the ship's redness in each frame, and creates a rectangle
object that represents where the ship is on the screen, and we will now use
it to detect when it is hit by an alien.
A couple of lines below this, under the line
alien_rect = get_sprite_rectangle(alien)
, write this:
if alien_rect.colliderect(ship_rect):
alien.hit = True
alien.x = alien.x - 6
alien.y = alien.y - 6
lives = lives - 1
ship.red = 255
continue
Before the line display_sprite(ship)
, write
this:
if ship.red > 0:
tmp = pygame.Surface(ship.image.get_size(), pygame.SRCALPHA, 32)
tmp.fill( (255, 255 - ship.red, 255 - ship.red, 255) )
tmp.blit(ship.image, (0,0), ship.image.get_rect(), pygame.BLEND_RGBA_MULT)
ship.image = tmp
This will actually use the number ship.red
to make the ship
as red as it needs to be!
Finally, we actually want to display the score on the screen! So
below the code which displays the player's score, write
the following:
lives_text = font.render("LIVES: " + str(lives), 1, foreground)
window.blit(lives_text, (10, 10))
Run Your Code
Try running your code now. You should now see that you have lives in the
top left hand corner, and when you hit an alien, your ship should flash
red and the players lives should decrease.
You should also notice that, when you have reached zero lives
you can still keep playing, and your lives will decrease even
further!
Prevent Negative Lives
This is quite an easy thing to fix, we only change the players life in one
place, and that is when the ship is hit by an alien. So we can add a check to
make sure that the game only detects this when the player has positive life.
Task:
In the loop which checks when aliens collide with the ship,
replace the line which says
if alien_rect.colliderect(ship_rect):
with this:
if alien_rect.colliderect(ship_rect) and lives > 0:
Run Your Code
Try running your code now. After your lives reaches 0
, the
game should not do anything when your ship is hit with aliens.
Destroy the ship when lives run out
Task:
Firstly we need to load the new destroyed ship image, under the
code where we load the other ship images, write:
ship_image_destroyed = pygame.image.load("ship_destroyed.png")
Under the line ship.red = 0
, write this:
ship.alpha = 0
Under the line where we change the value ship.red
on
each frame, write this:
ship.alpha = max(0, ship.alpha - 2)
Inside the for loop where we detect collisions, under the line
lives = lives - 1
, replace the line
ship.red = 255
with this:
if lives == 0:
ship.x = ship.x - 50
ship.alpha = 255
else:
ship.red = 255
And finally, at the code where you draw the ship and make it red,
before the line if ship.red > 0:
, write this:
if lives == 0:
tmp = pygame.Surface(ship_image_destroyed.get_size(), pygame.SRCALPHA, 32)
tmp.fill( (255, 255, 255, ship.alpha) )
tmp.blit(ship_image_destroyed, (0,0), ship_image_destroyed.get_rect(), pygame.BLEND_RGBA_MULT)
ship.image = tmp
Run Your Code
Try running your code now. When you die, your ship should be replaced
with an image of a destroyed one that slowly fades away.
Restart the game when space is pressed
(wait until ship fades)
Keep track of a high score
Work in Progress!
I have not yet finished writing this lesson. Thank you for your eagerness
though. This page is being updated constantly.
In the mean time, if you are interested in teaching python to students, I
would recommend CodeClub's
resources.