A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://realpython.com/asteroids-game-python/ below:

Build an Asteroids Game With Python and Pygame – Real Python

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Using Pygame to Build an Asteroids Game in Python

Do you want to create your own computer games but like Python too much to abandon it for a career as a game developer? There’s a solution for that! With the Pygame module, you can use your amazing Python skills to create games, from the basic to the very complex. Below, you’ll learn how to use Pygame by making a clone of the Asteroids game!

In this tutorial, you’ll learn how to build a complete game, including:

Click the link below to download the code for this project and follow along as you build your game:

Get the Source Code: Click here to get the source code you’ll use to build an Asteroids game in Python with Pygame in this tutorial.

Let’s get started!

Demo: Asteroids Game in Python

The game you’ll be making is a clone of the classic arcade game Asteroids. In it, you control a spaceship and shoot asteroids. If your spaceship collides with an asteroid, you lose. If you shoot down all asteroids, you win!

Project Overview

Your Asteroids game in Python will feature a single spaceship. The spaceship can rotate left and right as well as accelerate forward. When it’s not accelerating, it will continue moving with the velocity it had. The spaceship can also shoot bullets.

The game will use the following key mappings:

Key Action Right Rotate the spaceship right Left Rotate the spaceship left Up Accelerate the spaceship forward Space Shoot Esc Exit the game

There will also be six big asteroids in the game. When a bullet hits a big asteroid, it will split into two medium ones. When a bullet hits a medium asteroid, it will split into two small ones. A small asteroid won’t split but will be destroyed by a bullet.

When an asteroid collides with the spaceship, the spaceship will be destroyed, and the game will end in a defeat. When all asteroids are gone, the game will end in a victory!

The project will be broken into ten steps:

  1. Setting up Pygame for a Python project
  2. Handing input in the game
  3. Loading images and showing them on the screen
  4. Creating game objects with an image, a position, and some logic
  5. Moving the spaceship
  6. Moving the asteroids and detecting collisions with the spaceship
  7. Shooting bullets and destroying asteroids
  8. Splitting asteroids into smaller ones
  9. Playing sounds
  10. Handling the end of the game

Each step will provide links to all necessary resources.

Prerequisites

To build your Asteroids game, you’ll need some more advanced elements of Python. You should already be comfortable with the language itself as well as with concepts like classes, inheritance, and callbacks. If you need to refresh your knowledge on these topics, then check our Object-Oriented Programming (OOP) in Python.

The game will also use vectors to represent positions and directions, as well as some vector operations to move the elements on the screen. Pygame will take care of most of the math, and all the necessary concepts will be explained in this tutorial. However, if you want to know more, then you can check out Vector Addition.

The Pygame documentation can be useful if you want to understand some concepts in depth, but you’ll find everything you need to know in this tutorial.

Step 1: Pygame Setup

At the end of this step, you’ll have a small Python project that uses Pygame. It will display a window with a caption, filled with a blue color. This will be first step toward your Asteroids game. You won’t need any specific game development tools. Your favorite text editor and the command line will be enough.

Python Project

To organize your project, start by creating a folder for it:

As with any Python project, you should also create a virtual environment for your Asteroids game. You can read more about virtual environments in Python Virtual Environments: A Primer.

When you’re done, create a requirements.txt file and add a Pygame dependency. For this project, it’s recommended that you use the latest version, which will make your Asteroids game work seamlessly on Linux and macOS. Your file should look like this:

Next, install the dependencies:

You can check if Pygame was installed correctly by running this command:

If everything went well, then you should see a window with the Pygame Aliens game.

Pygame Code

Now it’s time to start working on your own code! In general, the structure of a Pygame program looks like this:

Line 3 starts a loop, called the game loop. Each iteration of this loop generates a single frame of the game and usually performs the following operations:

  1. Input handling: Input like pressed buttons, mouse motion, and VR controllers position is gathered and then handled. Depending on the game, it can cause objects to change their position, create new objects, request the end of the game, and so on.

  2. Game logic: This is where most of the game mechanics are implemented. Here, the rules of physics are applied, collisions are detected and handled, artificial intelligence does its job, and so on. This part is also responsible for checking if the player has won or lost the game.

  3. Drawing: If the game hasn’t ended yet, then this is where the frame will be drawn on screen. It will include all the items that are currently in the game and are visible to the player.

The general structure of a Pygame program isn’t complicated, and you could probably get away with putting it in a basic loop. However, considering that you might expand your Asteroids game in the future, it’s a good idea to encapsulate all these operations in a Python class.

Creating a class means that you need to pick a name for your game, but “Asteroids” is already taken. How about “Space Rocks”?

Create a space_rocks directory, and inside it create a file called game.py. This is where you’ll put the main class of your Asteroids game: SpaceRocks. The file should look like this:

Here’s what’s happening in the code, step by step:

This might seem like a lot of extra steps, but now your code is nicely structured and has methods with descriptive names. Next time you need to change something related to drawing, you’ll know to use _draw(). To add input handling, you’ll modify _handle_input(), and so on.

Note: Normally, you would extract variables like screen size and color to a constant at the beginning of your class. However, in a few steps, you’ll replace the color with an image, and you won’t use the size of the screen outside of this method. Because of that, you can leave the values as they are.

Next, create a __main__.py file in your space_rocks folder. This file will take care of creating a new instance of your game and starting it by running main_loop(). It should look like this:

The structure of your project should now look like this:

awesome_pygame_project/
|
├── space_rocks/
|   ├── __main__.py
|   └── game.py
|
└── requirements.txt

Go ahead and run the game:

You’ll see a window with a blue background:

Congratulations, you just created a Pygame project! However, there’s no exit condition at this point, so you still have to use Ctrl+C in the command line to quit it. That’s why you’ll learn about input handling next.

Step 2: Input Handling

At this point, you have the main loop of the game, ready to be filled with logic. At the end of this step, you’ll also have a scaffolding in place to start plugging in user controls.

Most input processing in Pygame happens within an event loop. In each frame, your program can get a collection of events that happened since the previous frame. That includes mouse movement, keypresses, and so on. Then, one by one, these events can be handled. In Pygame, the method for obtaining that collection is pygame.event.get().

The event you need right now is pygame.QUIT. It happens when someone requests the program to end, either by clicking Close or by pressing Alt+F4 on Windows and Linux or Cmd+W on macOS. Modify space_rocks/game.py by rewriting SpaceRocks._handle_input() like so:

Go ahead and test it. Run the game and click the little X in the corner or use a proper shortcut. The window will be closed, just like you would expect.

But you can go a step further. In the end, your game will be controlled with only the keyboard, not the mouse. How about closing the window by pressing a custom key?

There are other types of events in Pygame, and one of them is a keypress event. It’s represented by a pygame.KEYDOWN constant. Each such event has information about the key that was pressed stored in the event.key attribute. You can check constants for different keys in the Pygame documentation. In this example, to close the game by pressing Esc, you’ll use pygame.K_ESCAPE.

Modify the _handle_input() method again:

Now your game also closes when you press Esc.

You’ve managed to display a window and close it properly. But the window is still filled with a single color. Next, you’ll learn how to load an image and show it on the screen.

Step 3: Images

At this point, you have a game window that you can close by pressing a key. At the end of this step, you’ll display an image in that window.

Although you could make a computer game with only colored rectangles and other simple shapes, using images will make it much more attractive. In computer game development, images are usually called sprites. Of course, games use many more types of resources, like sounds, fonts, animations, and so on. Together, these resources are called assets.

As your game grows, it’s important for it to keep a proper structure. So, start by creating a folder called assets and, inside it, another called sprites. That’s where you’ll put all the sprites used by your game.

Next, download the image of the space background and put it in the assets/sprites folder. You can download the source code by clicking the link below:

Get the Source Code: Click here to get the source code you’ll use to build an Asteroids game in Python with Pygame in this tutorial.

Also, because images will be loaded many times in your program, it’s a good idea to extract this functionality to a separate method in a separate file. Create a file called space_rocks/utils.py that will keep all the reusable methods. Then implement image loading:

Here’s what happens:

Note: In general, you could just use convert_alpha() for all types of images since it can also handle an image without transparent pixels. However, drawing transparent images is a bit slower than drawing nontransparent ones.

Since computer games are all about performance, you’ll practice optimizing your games by choosing the correct type of image and increasing the speed of your game, even if by just a bit.

The structure of your project now looks like this:

awesome_pygame_project/
|
├── assets/
|   |
│   └── sprites/
│       └── space.png
|
├── space_rocks/
│   ├── __main__.py
│   ├── game.py
│   └── utils.py
|
└── requirements.txt

Now that your program can load images, it’s time to change the blue background into something more interesting. Edit the space_rocks/game.py file:

To display one surface on another in Pygame, you need to call blit() on the surface you want to draw on. This method takes two arguments:

  1. The surface that you want to draw
  2. The point where you want to draw it

It’s good to keep in mind that in Pygame, the coordinate system starts in the top-left corner. The x-axis goes from left to right, and the y-axis goes from top to bottom:

As you can see, the UP vector, pointing upwards, will have a negative y-coordinate.

The coordinates passed to blit() are given as two values: X and Y. They represent the point where the top-left corner of the surface will be located after the operation:

As you can see, the top-left corner is moved by the blit coordinates to calculate the correct position.

In your case, the new background has the same size as the screen (800 × 600 pixels), so the coordinates will be (0, 0), representing the top-left corner of the screen. That way, the background image will cover the entire screen.

Run your program now, and you’ll see a screen with a background image:

Your game now has a very nice background image, but nothing is happening there yet. Let’s change this by adding some objects.

Step 4: Controlling Game Objects

At this point, your program displays a background image of a small piece of cosmos where your Asteroids game will take place. It’s a bit empty right now, so in this section you’ll fill it up. You’ll create a class that represents other drawable game objects and use it to show a spaceship and an asteroid.

Advanced Behavior

You’ve already used surfaces, but Pygame also offers another class, Sprite, that’s intended as a base class for visible objects. It contains some useful methods, but you might also encounter a couple of limitations.

One limitation is that a game object is more than just a sprite. It contains additional data, like its direction and velocity. It also needs more advanced behaviors, like shooting bullets or playing sounds. Most of this additional information and behavior isn’t provided by the Sprite class, so you would need to add it on your own.

Another issue is that Pygame draws sprites starting from the top-left corner. In your game, it might be easier to store the central position of an object for the purpose of moving and rotating it. In that case, you’ll have to implement a way to transform that position into a top-left corner as required by Pygame.

Finally, although Pygame already has methods for detecting overlaps between images, they might not be good for detecting collisions between objects. A rotatable spaceship or an asteroid probably won’t fill the entire image, but rather the round area within it. In that case, the collision should take into account only that round area, not the entire surface of the sprite. Otherwise, you might get incorrect results:

In this example, the sprites collide, but game objects don’t.

This is actually where the Sprite class could help, since you can use it with pygame.sprite.collide_circle(). This method detects collisions between two sprites using circles centered on their surfaces. However, detecting a collision of circles isn’t a very complicated process, and you can implement it on your own.

Given these issues, it quickly becomes apparent that the built-it Pygame Sprite class is meant to be augmented, not simply used on its own. In the case of your game, Pygame sprites provide few useful features. It might be a good idea to implement a custom class for game objects instead. That should give you more control and help you understand some concepts since you’ll be implementing them on your own.

GameObject Class

In this section, you’ll introduce the GameObject class. It will encapsulate some generic behavior and data for all other game objects. Classes that represent specific objects (like the spaceship) will inherit from it and extend it with their own behavior and data. If you want to refresh your knowledge of classes and inheritance, then check out Object-Oriented Programming (OOP) in Python.

The GameObject class will store the following data:

Here’s a graphical representation of the game object:

The sprite will be a surface loaded with load_sprite() from the previous examples. The radius is an integer indicating the number of pixels from the center of the object to the edge of the collision zone. However, the position itself and the velocity will need a new type: a vector.

Vectors are similar to tuples. In a 2D world (like the one in your game), vectors are represented by two values indicating x- and y-coordinates. These coordinates can point to a position, but they can also represent motion or acceleration in a given direction. Vectors can be added, subtracted, or even multiplied to quickly update the position of a sprite. You can read more about vectors in Vectors in 2-dimensional Space.

Because of how useful vectors are in games, Pygame already has a class for them: Vector2 in the pygame.math module. It offers some additional functionality, like calculating the distance between vectors and adding or subtracting vectors. Those features will make your game logic much easier to implement.

In the space_rocks directory, create a new file called models.py. For now, it will store the GameObject class, but later you’ll add classes for asteroids, bullets, and the spaceship. The file should look like this:

Here’s a breakdown:

Please note that your game objects have a central position, but blit() requires a top-left corner. So, the blit position has to be calculated by moving the actual position of the object by a vector:

That process happens in draw().

You can test this by adding a spaceship and a single asteroid. First, copy the spaceship and asteroid images to assets/sprites. You can download the source code by clicking the link below:

Get the Source Code: Click here to get the source code you’ll use to build an Asteroids game in Python with Pygame in this tutorial.

The structure of your project should look like this:

awesome_pygame_project/
|
├── assets/
|   |
│   └── sprites/
│       ├── asteroid.png
│       ├── space.png
│       └── spaceship.png
|
├── space_rocks/
│   ├── __main__.py
│   ├── game.py
│   ├── models.py
│   └── utils.py
|
└── requirements.txt

Now modify the space_rocks/game.py file:

Both objects are placed in the middle of the screen, using the coordinates (400, 300). Both objects’ position will be updated each frame using _process_game_logic(), and they’ll be drawn using _draw().

Run this program, and you’ll see an asteroid moving to the right and a spaceship standing still in the middle of the screen:

You can also test collides_with() by temporarily adding one line at the end of _draw():

In the command line, you’ll notice how the method initially prints True since the asteroid covers the spaceship. Later, as the asteroid moves further to the right, it starts printing False.

Controlling the Speed

Now that you have moving objects on the screen, it’s time to think about how your game will perform on different machines with different processors. Sometimes it’ll run faster, and sometimes it’ll run slower.

Because of that, the asteroids (and soon bullets) will move with different speed, making the game sometimes easier and sometimes harder. That’s not something that you want. What you want is for your game to run with a fixed number of frames per second (FPS).

Luckily, Pygame can take care of that. It offers a pygame.time.Clock class with a tick() method. This method will wait long enough to match the desired FPS value, passed as an argument.

Go ahead and update space_rocks/game.py:

If you run your game now, then the asteroid might move at a different speed than it initially had. However, you can now be sure that this speed will remain the same, even on computers with super-fast processors. That’s because your game will always run at 60 FPS. You can also experiment with different values passed to tick() to see the difference.

You just learned how to show and move objects on the screen. Now you can add some more advanced logic to your game.

Step 5: Spaceship

At this point, you should have a class for general drawable and movable game objects. At the end of this step, you’ll use it to create a controllable spaceship.

The class you created in the previous step, GameObject, holds some general logic that can be reused by different game objects. However, each game object will also implement its own logic. The spaceship, for example, is expected to rotate and accelerate. It will also shoot bullets, but that comes later.

Creating a Class

The image of the spaceship is already in the space_rocks/assets directory that you added in Step 4. However, earlier it was used in the main game file, and now you need to load it in one of the models. To be able to do this, update the imports section in the space_rocks/models.py file:

Now you can create, in the same file, the Spaceship class that inherits from GameObject:

It doesn’t do a lot at this point—it just calls the GameObject constructor with a specific image and a zero velocity. However, you’ll soon add more functionality.

To use this new class, you first need to import it. Update the imports in the space_rocks/game.py file like this:

You probably noticed that the original import of the GameObject class is gone. That’s because GameObject is used as a base class to be inherited by other classes. You shouldn’t use it directly, but rather import the classes that represent actual game objects.

This means that the asteroid from the previous step will stop working, but that’s not a big issue. You’ll soon add a proper class that represents asteroids. Until then, you should focus on the spaceship.

Go ahead and edit the SpaceRocks class to look like this:

Two things happened:

  1. In line 7, you replaced the base GameObject class with a dedicated Spaceship class.

  2. You removed all self.asteroid references from __init__(), _process_game_logic() and _draw().

If you run your game now, then you’ll see a spaceship in the middle of the screen:

The changes didn’t add any new behavior yet, but now you have a class that you can extend.

Rotating the Spaceship

By default, the spaceship is facing up, toward the top of the screen. Your players should be able to rotate it left and right. Luckily, Pygame has built-in methods for rotating sprites, but there’s a small problem.

In general, image rotation is a complex process that requires recalculating pixels in the new image. During that recalculation, information about the original pixels is lost and the image is deformed a bit. With each rotation, the deformation becomes more and more visible.

Because of that, it might be a better idea to store the original sprite in the Spaceship class and have another sprite, which will be updated every time the spaceship rotates.

For that approach to work, you’ll need to know the angle by which the spaceship is rotated. This can be done in two ways:

  1. Keep the angle as a floating point value and update it during rotation.
  2. Keep the vector representing the direction the spaceship is facing and calculate the angle using that vector.

Both ways are good, but you need to pick one before you proceed. Since the position and the velocity of the spaceship are already vectors, it makes sense to use another vector to represent the direction. That will make it more straightforward to add vectors and update the position later. Luckily, the Vector2 class can be rotated very easily, and the result won’t be deformed.

First, create a constant vector called UP in the space_rocks/models.py file. You’ll use it as a reference later:

Remember that Pygame’s y-axis goes from top to bottom, so a negative value actually points upwards:

Next, modify the Spaceship class:

The MANEUVERABILITY value determines how fast your spaceship can rotate. You learned earlier that vectors in Pygame can be rotated, and this value represents an angle in degrees by which your spaceship’s direction can rotate each frame. Using a larger number will rotate the spaceship faster, while a smaller number will allow more granular control over the rotation.

Next, add a direction to the Spaceship class by modifying the constructor:

The direction vector will initially be the same as the UP vector. However, it will be modified later, so you need to create a copy of it.

Next, you need to create a new method in the Spaceship class called rotate():

This method will change the direction by rotating it either clockwise or counterclockwise. The rotate_ip() method of the Vector2 class rotates it in place by a given angle in degrees. The length of the vector doesn’t change during this operation. You can learn a bit more about the advanced math behind 2D vector rotation from Rotating Points Using Rotation Matrices.

All that’s left is to update the drawing of the Spaceship. To do this, you first need to import rotozoom, which is responsible for scaling and rotating images:

Then, you can override the draw() method in the Spaceship class:

Here’s a step-by-step breakdown:

Note that rotozoom() returns a new surface with a rotated image. However, in order to keep all the contents of the original sprite, the new image might have a different size. In that case, Pygame will add some additional, transparent background:

The size of the new image can be significantly different than that of the original image. That’s why draw() recalculates the blit position of rotated_surface. Remember that blit() starts in the upper-left corner, so to center the rotated image, you also need to move the blit position by half the size of the image.

Now you need to add input handling. However, the event loop won’t exactly work here. Events are recorded when they happen, but you need to constantly check if a key is pressed. After all, the spaceship should accelerate for as long as you press Up, and it should rotate constantly when you press Left or Right.

You could create a flag for each key, set it when the key is pressed, and reset when it’s released. However, there’s a better way.

The current state of the keyboard is stored in Pygame and can be obtained using pygame.key.get_pressed(). It returns a dictionary where key constants (like pygame.K_ESCAPE that you used previously) are keys, and the value is True if the key is pressed or False otherwise.

Knowing this, you can edit the space_rocks/game.py file and update the _handle_input() method of SpaceRocks class. The constants you need to use for arrow keys are pygame.K_RIGHT and pygame.K_LEFT:

Now your spaceship will rotate left and right when you press arrow keys:

As you can see, the spaceship rotates correctly. However, it still doesn’t move. You’ll fix that next.

Accelerating the Spaceship

In this section, you’ll add acceleration to your spaceship. Remember that, according to the game mechanics of Asteroids, the spaceship can only move forward.

In your game, when you press Up, the spaceship’s speed will increase. When you release the key, the spaceship will maintain its current speed but should no longer accelerate. So in order to slow it down, you’ll have to turn the spaceship around and press Up again.

The process might already seem a bit complicated, so before you proceed, here is a short recap:

You can calculate the change in velocity by multiplying the direction vector by the ACCELERATION value and adding the result to the current velocity. This happens only when the engine is on—that is, when the player presses Up. The new position of the spaceship is calculated by adding the current velocity to the current position of the spaceship. This happens each frame, regardless of the engine status.

Knowing this, you can add the ACCELERATION value to the Spaceship class:

Then, create accelerate() in the Spaceship class:

Now you can add input handling to _handle_input() in SpaceRocks. Similarly to the rotation, this will check the current state of the keyboard, not the keypress events. The constant for Up is pygame.K_UP:

Go ahead and test this. Run your game, rotate the spaceship, and turn on the engine:

Your spaceship can now move and rotate! However, when it reaches the edge of the screen, it just keeps moving. That’s something you should fix!

Wrapping Objects Around the Screen

An important element of this game is making sure that game objects don’t leave the screen. You can either have them bounce back off the edge or make them reappear on the opposite edge of the screen. In this project, you’ll implement the latter.

Start by importing the Vector2 class in space_rocks/utils.py file:

Next, create wrap_position() in the same file:

By using the modulo operator on line 4, you make sure that the position never leaves the area of the given surface. In your game, that surface will be the screen.

Import this new method in space_rocks/models.py:

Now you can update move() in the GameObject class:

Notice that using the wrap_position() isn’t the only change here. You also add a new surface argument to this method. That’s because you need to know the area around which the position should be wrapped. Remember to update the method call in the SpaceRocks class as well:

Now your spaceship reappears on the other side of the screen.

The logic of moving and rotating the spaceship is ready. But the ship is still alone in the empty space. Time to add some asteroids!

Step 6: Asteroids

At this point, you have a single spaceship that you can move on the screen. At the end of this step, your game will also show some asteroids. Moreover, you’re going to implement collisions between the spaceship and the asteroids.

Creating a Class

Similar to Spaceship, you’ll start by creating a class called Asteroid that inherits from GameObject. Edit the space_rocks/models.py file like so:

Just like before, you start by calling the GameObject constructor with a specific image. You added the image in one of the previous steps.

Next, import the new class in space_rocks/game.py:

Finally, edit the constructor of the SpaceRocks class in the same file to create six asteroids:

Now that you have more game objects, it would be a good idea to create a helper method in the SpaceRocks class that returns all of them. This method will then be used by the drawing and moving logic. That way, you can later introduce new types of game objects and modify only this single method, or you can exclude some objects from this group if necessary.

Call this method _get_game_objects():

Now use it to move all game objects in a single loop by editing _process_game_logic():

The same goes for _draw():

Run your game now and you should see a screen with the asteroids:

Unfortunately, all the asteroids are piled up in one corner of the screen.

Well, that was kind of expected, since all the asteroids are created with a position of (0, 0), which represents the top-left corner. You can change this by setting a random position on the screen.

Randomizing the Position

To generate a random position, you’ll have to add some imports to space_rocks/utils.py file:

Then, create a method called get_random_position() in the same file:

This will generate a random set of coordinates on a given surface and return the result as a Vector2 instance.

Next, import this method in the space_rocks/game.py file:

Now use get_random_position() to place all six asteroids in random locations. Modify the constructor of the SpaceRocks class:

Now when you run the game, you’ll see a nice, random distribution of asteroids on the screen:

This looks much better, but there’s a small problem: the asteroids were generated in the same area as the spaceship. After you add collisions, this would cause the player to lose immediately after starting the game. That would be very unfair!

One solution to this problem is to check if the position is too close to the spaceship, and if so, generate a new one until a valid position is found.

Start by creating a constant representing an area that has to remain empty. A value of 250 pixels should be enough:

Now you can modify the constructor of the SpaceRocks class to make sure that your players always have a chance to win:

In a loop, your code checks if the position of an asteroid is larger than the minimal asteroid distance. If not, then the loop runs again until such a position is found.

Run the program again, and none of the asteroids will overlap with the spaceship:

You can run the game several times to make sure that each time there’s some free space around the spaceship.

Moving the Asteroids

At the moment, your program shows six asteroids at random positions, and you’re ready to spice things up a bit by moving them! Similar to the position, the velocity of an asteroid should also be random, not only in terms of the direction, but also value.

Start by creating a method called get_random_velocity() in the space_rocks/utils.py file:

The method will generate a random value between min_speed and max_speed and a random angle between 0 and 360 degrees. Then it will create a vector with that value, rotated by that angle.

Because the asteroid’s velocity should be random no matter where it is placed, let’s use this method directly in the Asteroid class. Start with updating the imports in the space_rocks/models.py file:

Note that you’re setting a your random position in one place and your random velocity somewhere else. That’s because the position should be random only for the six asteroids you start with, so it’s being set in in the space_rocks/game.py file, where the game is initialized. However, the velocity is random for every asteroid, so you set it in the constructor of the Asteroid class.

Then use the new method in the constructor of the Asteroid class:

Notice that the method uses the minimum value of 1. That’s because the asteroid should always move, at least a bit.

Run your game again to see moving asteroids:

You can also move the spaceship around the screen. Unfortunately, when it encounters an asteroid, nothing happens. It’s time to add some collisions.

Colliding With the Spaceship

A very important part of this game is the possibility of your spaceship being destroyed by an asteroid collision. You can check the collisions using GameObject.collides_with() introduced in Step 4. All you need to do it call this method for each asteroid.

Edit the _process_game_logic() method in the SpaceRocks class like this:

If any of the asteroids collides with the spaceship, then the spaceship is destroyed. In this game, you’ll represent this by setting self.spaceship to None.

Notice that there’s also a check for self.spaceship at the beginning of the loop. That’s because, when the spaceship is destroyed, there’s no reason to check any collisions with it. Also, detecting a collision with a None object would result in an error.

Now that it’s possible for the spaceship to have a value of None, it’s important to update _get_game_objects() in the SpaceRocks class to avoid trying to render or move a destroyed spaceship:

The same goes for input handling:

You can run your game now and see that the spaceship disappears after colliding with an asteroid:

Your spaceship can now fly around and be destroyed when it collides with asteroids. You’re ready to make it possible for the asteroids to be destroyed too.

Step 7: Bullets

At this point, you have some randomly placed and moving asteroids and a spaceship that can move around and avoid them. At the end of this step, your spaceship will also be able to defend itself by shooting bullets.

Creating a Class

Start with adding an image of a bullet to assets/sprites. You can download the source code by clicking the link below:

Get the Source Code: Click here to get the source code you’ll use to build an Asteroids game in Python with Pygame in this tutorial.

The structure of your project should look like this:

awesome_pygame_project/
|
├── assets/
|   |
│   └── sprites/
│       ├── asteroid.png
│       ├── bullet.png
│       ├── space.png
│       └── spaceship.png
|
├── space_rocks/
│   ├── __main__.py
│   ├── game.py
│   ├── models.py
│   └── utils.py
|
└── requirements.txt

Then edit the space_rocks/models.py file by creating a class called Bullet that inherits from GameObject:

Just like before, this will only call the GameObject constructor with a specific sprite. However, this time the velocity will be a required argument because a bullet has to move.

Next, you should add a way to keep track of the bullets, similar to what you did for the asteroids. Edit the constructor of the SpaceRocks class in the space_rocks/game.py file:

Bullets should be treated the same way as other game objects, so edit the _get_game_object() method in SpaceRocks:

The list of bullets is there, but it’s empty for now. You can fix that.

Shooting a Bullet

There’s a small issue with shooting. Bullets are stored in the main game object, represented by the SpaceRocks class. However, the shooting logic should be determined by the spaceship. It’s the spaceship that knows how to create a new bullet, but it’s the game that stores and later animates the bullets. The Spaceship class needs a way to inform the SpaceRocks class that a bullet has been created and should be tracked.

To fix this, you can add a callback function to the Spaceship class. That function will be provided by the SpaceRocks class when the spaceship is initialized. Every time the spaceship creates a bullet, it will initialize a Bullet object and then call the callback. The callback will add the bullet to the list of all bullets stored by the game.

Start by adding a callback to the constructor of the Spaceship class in the space_rocks/models.py file:

You’ll also need the value of the bullet’s speed:

Next, create a method called shoot() in the Spaceship class:

You start by calculating the velocity of the bullet. The bullet is always shot forward, so you use the direction of the spaceship multiplied by the speed of the bullet. Because the spaceship doesn’t necessarily stand still, you add its velocity to the velocity of the bullet. That way, you can create high-speed bullets if the spaceship is moving very fast.

Then you create an instance of the Bullet class at the same location as the spaceship, using the velocity that was just calculated. Finally, the bullet is added to all the bullets in the game by using the callback method.

Now add the callback to the spaceship when it’s created. Bullets are stored as a list, and the only thing the callback has to do is add new items to that list. Therefore, the append() method should do the job. Edit the constructor of the SpaceRocks class in the space_rocks/game.py file:

The last thing you need to add is input handling. The bullet should be generated only when Space pressed, so you can use the event loop. The constant for Space is pygame.K_SPACE.

Modify the _handle_input() method in the SpaceRocks class:

Notice that the new input handling also checks if the spaceship exists. Otherwise, you could encounter errors when trying to call shoot() on a None object.

Run your game now and shoot some bullets:

Your spaceship can finally shoot! However, the bullets don’t leave the screen, which might be an issue.

Wrapping the Bullets

At the moment, all game objects are wrapped around the screen. That includes bullets. However, because of this wrapping, the screen quickly gets filled with bullets flying in all directions. That might make the game a bit too easy!

You can solve this issue by disabling the wrapping only for bullets. Override move() in the Bullet class in the space_rocks/models.py file like this:

That way the bullets won’t wrap around the screen. However, they also won’t be destroyed. Instead, they’ll continue flying into the infinite abyss of the cosmos. Soon, your list of bullets will contain thousands of elements, and all of them will be processed in each frame, resulting in a decline of the performance of your game.

To avoid that situation, your game should remove the bullets as soon as they leave the screen. Update the _process_game_logic() method of the SpaceRocks class in the space_rocks/game.py file:

Notice that instead of using the original list, self.bullets, you create a copy of it using self.bullets[:] in line 11. That’s because removing elements from a list while iterating over it can cause errors.

Surfaces in Pygame have a get_rect() method that returns a rectangle representing their area. That rectangle, in turn, has a collidepoint() method that returns True if a point is included in the rectangle and False otherwise. Using these two methods, you can check if the bullet has left the screen, and if so, remove it from the list.

Colliding With Asteroids

A crucial element of your bullets is still missing: the ability to destroy asteroids! You’ll fix that in this section.

Update the _process_game_logic() method of the SpaceRocks class like this:

Now, whenever a collision is detected between a bullet and an asteroid, both will be removed from the game. Notice that, just like before in the bullet loop, you don’t use the original lists here. Instead, you create copies using [:] in lines 11 and 12.

If you run your game now and take a good aim when shooting, then you should be able to destroy some asteroids:

Your spaceship can finally protect itself! However, there are only six big targets in the game. Next, you’ll make it a bit more challenging.

Step 8: Splitting the Asteroids

At this point, you have a game with a spaceship, asteroids, and bullets. At the end of this step, your asteroids will split when hit by a bullet. A big asteroid will turn into two medium ones, a medium one will turn into two small ones, and a small one will disappear.

The size of an asteroid will be represented by a number:

Asteroid size Asteroid type 3 Big asteroid 2 Medium asteroid 1 Small asteroid

Each time an asteroid is hit, it will produce two asteroids with a smaller size. The exception is an asteroid with a size 1, as it should not produce any new asteroids.

The size of an asteroid will also determine the size of its sprite, and consequently its radius. In other words, the asteroids will be scaled like this:

Asteroid size Asteroid scale Description 3 1 The default sprite and radius 2 0.5 Half the default sprite and radius 1 0.25 One-quarter of the default sprite and radius

This might seem a bit complicated, but you can do it with just a few lines of code. Rewrite the constructor of the Asteroid class in the space_rocks/models.py file:

This method will assign a size to an asteroid, using the default value 3, which represents a big asteroid. It will also scale the original sprite by using rotozoom(). You’ve used it before for rotating the spaceship. This method can also be used for scaling if the angle is 0 and the scale is anything other than 0. In this example, the size_to_scale lookup table contains scales for different sizes:

Size Scale 3 1 2 0.5 1 0.25

Finally, you pass the scaled sprite to the constructor of the GameObject class, which will take care of calculating the radius based on the new image size.

Your new logic requires an asteroid to be able to create new asteroids. The situation is similar to the spaceship and bullets, so you can use a similar solution: a callback method.

Update the constructor of the Asteroid class again:

Now you can create a method called split() in the same class:

This will create two new asteroids at the same position as the current one. Each of them will have a slightly smaller size. This logic will happen only if the current asteroid is a medium or large one.

Now you can add the callback to each newly created asteroid in the constructor of the SpaceRocks class. Just like in the case of the spaceship, you’ll use the append() method of the proper list:

Remember to call split() when an asteroid gets hit by a bullet. Update the _process_game_logic() method of the SpaceRocks class:

If you run your game now and shoot down some asteroids, then you’ll notice that, instead of disappearing right away, they split into smaller ones:

You just implemented the entire logic of the game! The spaceship can move, it gets destroyed after colliding with an asteroid, it shoots bullets, and asteroids split into smaller ones. But the game is silent at the moment. You’ll take care of that next.

Step 9: Playing Sounds

At this point, your program displays all the game objects and handles interactions between them. At the end of this step, your game will also play sounds.

In Step 7, the spaceship was equipped with a weapon. That weapon is, however, completely silent. This is very accurate in terms of physics, since sounds don’t travel in a vacuum (“In space no one can hear you scream”). Nevertheless, using sounds in your game would make it much more attractive.

First, create an assets/sounds directory and add the laser sound there. You can download the source code by clicking the link below:

Get the Source Code: Click here to get the source code you’ll use to build an Asteroids game in Python with Pygame in this tutorial.

Your project’s structure should look like this:

awesome_pygame_project/
|
├── assets/
|   |
│   ├── sounds/
│   │   └── laser.wav
|   |
│   └── sprites/
│       ├── asteroid.png
│       ├── bullet.png
│       ├── space.png
│       └── spaceship.png
|
├── space_rocks/
│   ├── __main__.py
│   ├── game.py
│   ├── models.py
│   └── utils.py
|
└── requirements.txt

Now you need to load the file. In Pygame, a sound is represented by the Sound class from the pygame.mixer module. Although you will only use a single sound in this game, you might want to add more later. That’s why you’ll create a helper method for loading sounds, similar to the one you created for sprites.

First, import the Sound class in the space_rocks/utils.py file:

Next, create a method called load_sound() in the same file:

The method has a similar logic to load_sprite(). It will assume that the sound is always located in the assets/sounds directory and that it’s a WAV file.

You can now import this new method in the space_rocks/models.py file:

Then load the sound in the constructor of the Spaceship class:

Finally, you should play the sound whenever the spaceship shoots. Update shoot():

Run the game now and you’ll hear a sound every time you shoot.

You’ve just learned how to work with audio files in Pygame! All that’s left is displaying a message at the end of the game.

Step 10: Ending the Game

At this point, your game is almost complete, with input handling, interactions, images, and even sounds. At the end of this step, you’ll also display the status of the game on the screen.

Many games show some additional information, both during the game and after it’s over. This can be a number of remaining hit points, a shield level, an ammo count, a total score for the mission, and so on. In this game, you’ll display the status of the game.

If the spaceship is destroyed by an asteroid, then the message You lost! should appear on the screen. But if all the asteroids are gone and the spaceship is still there, then you should display You won!

Pygame doesn’t have any advanced tools for drawing text, which means more work for the programmer. Rendered text is represented by a surface with a transparent background. You can manipulate that surface the same way you do with sprites, for example by using blit(). The surface itself is created using a font.

The full process of working with text in Pygame looks like this:

  1. Create a font: The font is represented by the pygame.font.Font class. You can use a custom font file, or you can use the default font. For this game, you’ll do the latter.

  2. Create a surface with the text: This is done using Font.render(). You’ll learn more about that method later in this tutorial. For now, it’s enough to know that it creates a surface with the rendered text and a transparent background.

  3. Blit the surface onto the screen: As with any other surface in Pygame, the text will only be visible if you blit it onto the screen or another surface that will eventually be shown on the screen.

Your font will be rendered with a color. In Step 1, you created a color using three values: red, green, and blue. In this section, you’ll use a Color class instead. Start by importing it into the space_rocks/utils.py file:

Then, create a print_text() method in the same file:

Here’s what’s happening:

Now you can import this method in the space_rocks/game.py file:

Now you need to create a font. You should also store the message that will be displayed. Edit the constructor of the SpaceRocks class:

The constructor of the Font class takes two arguments:

  1. The name of the font file, where None means that a default font will be used
  2. The size of the font in pixels

The content of the message needs to be set properly. When the spaceship is destroyed, set it to "You lost!". When all the asteroids are destroyed, set it to "You won!". Edit the _process_game_logic() method of the SpaceRocks class:

The last thing you need to do is actually display the message on the screen. Update the _draw() method of the SpaceRocks class:

Go ahead and test it. Start the game and crash the spaceship into an asteroid:

The game correctly shows a the message You lost!.

Now put some more effort and try to destroy all the asteroids. If you manage to do this, then you should see a victory screen:

In this step, you’ve learned how to display a text message on the screen. That was the last step of this tutorial. Your game is now complete!

Conclusion

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 tutorial, you’ve learned how to:

You went through the entire process of designing a game, structuring files, importing and using assets, and coding the logic. You can use all that knowledge for all your amazing future projects!

Click the link below to download the code for this project and follow along as you build your game:

Get the Source Code: Click here to get the source code you’ll use to build an Asteroids game in Python with Pygame in this tutorial.

Next Steps

Your Asteroids game in Python is complete, but there are so many features that you can add. Here are a few ideas to get you started:

What other ideas can you come up with to extend this project? Be creative and have fun! In this case, as they say, space is the limit 😃

If you’re interested in learning more about game development in Python, then here are some additional resources:

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Using Pygame to Build an Asteroids Game in Python


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4