Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Using Pygame to Build an Asteroids Game (Summary)

Congratulations, you just built a clone of the Asteroids game using Python! With Pygame, your Python knowledge can be directly translated into game development projects.

In this course, you’ve learned how to:

  • Load images and display them on the screen
  • Add input handling to your game
  • Implement game logic and collision detection in Python
  • Play sounds
  • Display text on the screen

For more information on concepts covered in this lesson, you can check out:

Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Already a member? Sign-In

Locked learning resources

The full lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Already a member? Sign-In

00:00 In the previous lesson, I showed you how to add text to the screen. In this lesson, I’ll wrap up the course.

00:07 This course has been all about creating your first game in Python using the Pygame library. By building an Asteroids clone called Space Rocks, you’ve learned about game loops and the main Pygame library methods, what sprites are and how to blit them, rotate them, and move them around.

00:25 You’ve also learned how to respond to keyboard input, how to play sounds, all about loading fonts and displaying text, and general game writing awesomeness.

00:37 Here are some ideas you can try to improve your game even further. In the lesson where ship collisions were added, I pointed out that using None for a destroyed ship meant there was a danger of crashing your code. I walked you through fixing a couple of spots but left one out. If you didn’t find it, I’ll tell you now. Fire the gun after the ship is destroyed, and the code will crash.

00:59 The simple way would be adding a None check inside the keyboard handler that fires the gun. Or, the better long-term way, that involves adding an alive and dead state to the ship class.

01:12 You would then modify the class further to behave correctly after the ship is in the dead state. This method takes a bit more work, but is probably the better solution, particularly if you’re going to… add more lives to the game!

01:27 This can be about more than just allowing the player to restart three times. You can also add a "press space to start" message with the game paused after each time the player respawns. Right now, it is possible if you accelerate in just the right way, for the ship to overtake the bullets. If you manage this, you’ll discover that these are magic bullets that only destroy rocks.

01:48 You could add some code to make them fatal to the ship.

01:52 Or, what’s a game without a score? Add something to track the score and display scoring text. You’ll need to update print_text() to do this, as right now, it only displays text as a centered message on the screen.

02:05 You can also add a play again key. When the game is done, the player should be able to play again without having to close the window and restart the program. And finally, if you want a real challenge, add an instant replay movie. Show the player what happened and how they lost.

02:22 This one could get messy. You could store all the positions of all the objects in order to create the new frames for, say, the last three seconds. Or, in a more memory-intensive version, you could store copies of the frames in buffers. When a user dies or wins, you then create a playback using the position information or the buffers.

02:40 Of course, you’d also want the player to be able to skip past this playback and get on with the game. This one could be fun to code.

02:49 Here are two great sources for more Pygame goodness. This is a link to the Pygame documentation, which is a great way to find all the ins and outs of writing Pygames.

02:59 And this is Christopher Bailey’s course on writing a 2D side-scroller in Pygame. It covers some of the same basics as this course in more detail, and it also talks about sprite groups, playing background music, and more.

03:13 That’s it for Space Rocks. I hope you enjoyed the course. Thanks for your attention. Leave a comment below bragging about any neat features you’ve added to your version of the game and challenge your fellow Pythonistas.

Avatar image for Rok Kužner

Rok Kužner on Jan. 1, 2022

Thanks! 😀

Avatar image for froyathehen

froyathehen on April 25, 2022

Christopher Trudeau, not only was it educational but a great deal of amusement! Looking forward to getting through some more content of yours ^^ Thanks!

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on April 26, 2022

Glad to hear you enjoyed it @froyathehen and happy that the jokes aren’t falling completely flat.

Avatar image for AntonB

AntonB on May 18, 2023

Hi Christopher and team - I really enjoyed this intro to pygame. It was informative and fun, and I’m old enough to get the jokes :) I’m looking forward to exploring more RP content!

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on May 18, 2023

Hi Anton,

Glad you enjoyed the course. You might also like Christopher Bailey’s PyGame course. There is some overlap of the concepts, but the game is different, so you get to see someone else’s coding style for a similar problem:

realpython.com/courses/pygame-primer/

Avatar image for JCode888

JCode888 on June 24, 2023

Hi Christopher!

This was a great course! I really enjoyed it!

I decided to try and make mine a more faithful looking clone to the original Asteroids. I created my own ship, asteroid and bullet in Adobe Illustrator and exported them as PNGs to use as my assets. The only slight problem I’ve run into is that the code that breaks the asteroids scales down the outline around them, which makes them look a little broken up in the smaller size asteroids. I tried increasing the “stroke”(outline) around them in Illustrator before exporting, which works somewhat, but I don’t want to make them look too “chunky” as I’m trying to get it to look as close to the original as possible. I believe the original used vector graphics, which probably doesn’t change the look when scaling, unlike scaling a PNG.

I also used my keyboard and Ableton Live to record my own shooting and asteroid explosion sound effects. I was proud of myself for figuring out where to best add the code so that the asteroids always make the explosion noise when they are hit by a bullet. :)

I definitely want to try out some of your suggestions for improvements like fixing the firing after the ship is gone crash, adding more lives and adding a score. Hopefully I can figure all of that out.

Thanks again for a really fun course!

Avatar image for JCode888

JCode888 on June 25, 2023

I’m trying to implement a score option. I feel like I’m really close, but have tried a ton of things and just can’t seem to get it to display and update on the screen. In utils.py, I added:

def print_score_text(surface, text, font, color=Color("white")):
    text_surface = font.render(text, True, color)
    rect = text_surface.get_rect()
    rect.center = Vector2(40, 40)
    surface.blit(text_surface, rect)

basically copying the format of the print_text function. In game.py, I’ve added:

import pygame
from models import Rock, Spaceship
from utils import load_sprite, load_sound, print_text, print_score_text

bullets = []
rocks = []
score = 0

class Asteroids:
    def __init__(self):
        # Initialize pygame and set the title
        pygame.init()
        pygame.display.set_caption("Asteroids")
        self.clock = pygame.time.Clock()
        self.font = pygame.font.Font(None, 64)
        self.score_font = pygame.font.Font(None, 24)
        self.message = ""
        self.score_message = str(score)

importing the print_score_text function, initializing the score variable at 0, setting the size of the font and saying that self.score_message = str(score).

In the game_logic function, I add a global score variable and within the if statement that deals with bullets colliding with the asteroids, I increase the score variable by 20 each time. I know that’s working because I used a print statement to debug and I can see the score increase in the Command Prompt every time I shoot an asteroid.

    def _game_logic(self):
        global bullets, rocks, score

        for obj in self.game_objects:
            obj.move(self.screen)

        rect = self.screen.get_rect()
        for bullet in bullets[:]:
            if not rect.collidepoint(bullet.position):
                bullets.remove(bullet)

        for bullet in bullets[:]:
            for rock in rocks[:]:
                if rock.collides_with(bullet):
                    score += 20
                    print(score)
                    self.explode.play() #play explosion noise when bullet hits asteroid
                    rocks.remove(rock)
                    rock.split()
                    bullets.remove(bullet)
                    break

Then in the draw function, I call print_score_text below where the regular print_text function is called.

    def _draw(self):
        self.screen.blit(self.background, (0, 0))

        for obj in self.game_objects:
            obj.draw(self.screen)

        if self.message:
            print_text(self.screen, self.message, self.font)

        print_score_text(self.screen, self.score_message, self.score_font)

        pygame.display.flip()
        self.clock.tick(30)

I’ve tried a hundred things…putting that inside the if statement, outside, outside the draw funtion, making score global in a bunch of different functions etc. Nothing I do seems to make the score update on the screen. It just shows the initial 0 score even though I can see it increase with the print statements in the shell. I don’t know what else to try. Any help is much appreciated!

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on June 25, 2023

Hi JCode888,

First off, it has been almost three years since I wrote this code, so my memory is a little fuzzy.

I don’t immediately see anything wrong with your print_score_text function, and it definitely should be called inside of the _draw method, but, I’m hazy on exactly how the coordinate system works (different graphical environments do it differently), so my first inclination is to ask whether you’re sure you’re drawing it on the screen and not off screen.

Second, I see the place where you initialize .score_message but I don’t see where it gets updated. I’m not sure whether that’s because you’re assuming it gets updated automatically, or because it just isn’t included in the code you showed. If it is the former, you need to update the .score_message string each and every time you change .score integer. As you have a special function for printing the score, I might just make the value passed in be the integer itself and convert it into the string inside the function. No need to keep a separate “.score_message” attribute on the object at all.

One way to help debug this kind of thing, is to start with what works. For example, copy the print_text method exactly, rename it, have the score show up in the middle of the screen like the “end game” message, and see if it updates. It will be in the way of the play, but then you can figure out how to make incremental changes – font size, position, colour, whether the message is updating, etc.

By doing one change at a time, it is easier to track down what is working and what isn’t.

Glad you’re having fun with it. Hopefully you squish this bug soon. Good luck.

Avatar image for JCode888

JCode888 on June 26, 2023

Thank you so much for your help Christopher!

The score was definitely appearing on screen. In the print_score_text function, I used:

rect.center = Vector2(40, 40)

to place it in the top left corner of the screen. It was appearing, but only as 0, which is what I originally initialized the score variable at.

I used:

self.score_message = str(score)

in my Asteroids class which I thought would update it. But, you mentioning not seeing where it is updated got me thinking that the updating needs to happen inside the game_logic function. (though, I originally thought having score += 20 in the game_logic function was updating it since I already set self.score_message = str(score) in the Asteroids class.

So now, I just initialize self.score_message = "00" inside the Asteroids class. Then I use self.score_message = str(score) inside the game_logic function....and it works!!

One step closer to a fairly close Asteroids clone! Now I just have to figure out how to make multiple lives! Thank you for your help! :)

Avatar image for Christopher Trudeau

Christopher Trudeau RP Team on June 27, 2023

Glad that fixed it JCode888.

Become a Member to join the conversation.