Space Shooter Game

Screenshot

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.

import pygame
import sys

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.

pygame.init()

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:

my_variable = 4

… 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.

    clock.tick(50)

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:

Space Ship in Corner

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:

x-y

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:

moved ship

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:

moved ship off screen

You can see it has moved off screen.

Lets look at the final line of code we added:

    pygame.display.flip()

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 10 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.

Shooting Ship

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.

Tilting Ship

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.

First Contact

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:

Random Encounters

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:

Random Encounters at Random Heights

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.

Stars

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.

Stars of Different 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.

Score Text

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!

Introducing Lives

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.

Exploding Ship

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.