We had great fun creating our basic Space Invaders clone, Pivaders. One of the key challenges with the project was keeping it to a manageable size – just 300 lines of Python. Without the use of Pygame’s strong set of features, that goal would likely have been overshot at least twofold. Pygame’s ability to group, manage and detect collisions thanks to the Sprite class really made a great difference to our project, not just in terms of length but in simplicity. If you missed the first part of the project, you can find the v0.1 code listing on GitHub, while you can find version v0.2 of the code, including all the images, music and sound effects in the same repo, as well as on this month’s cover disc.
Even working within the clearly defined framework Pygame offers, there are still a thousand ways we could have approached adding animation and sound. We could have created any one of a dozen classes to create and manage containers of individual images, or read in a sprite sheet (a single image full of smaller, separate images) which we could then draw (or blit) to the screen. For the sake of simplicity and performance, we integrated a few animation methods into our Game class and opted to use a sprite sheet. Not only does it make it very easy to draw to the screen, but it also keeps the asset count under control and keeps performance levels up, which is especially important for the Raspberry Pi.
What you’ll need
01 Setting up dependencies
As we recommended with last issue’s tutorial, you’ll get much more from the exercise if you download the code and use it for reference as you create your own animations and sound for your Pygame projects. Regardless of whether you just want to simply preview and play or walk-through the code to get a better understanding of basic game creation, you’re still going to need to satisfy some basic dependencies. The two key requirements here are Pygame and Git, both of which are installed by default on up-to-date Raspbian installations. If you’re unsure if you have them, though, type the following at the
sudo apt-get install python- pygame git
Step 02 Downloading pivaders
Git is a superb version control solution that helps programmers safely store their code and associated files. Not only does it help you retain a full history of changes, it means you can ‘clone’ entire projects to use and work on from places like github.com. To clone the version of the project we created for this tutorial, go to your home folder from the command line (cd ~) and type:
git pull https://github.com/russb78/pivaders.git
This will create a folder called pivaders – go inside (cd pivaders) and take a look around.
Step 03 Navigating the project
The project is laid out quite simply across a few subfolders. Within pivaders sits a licence, readme and a second pivaders folder. This contains the main game file, pivaders.py, which launches the application. Within the data folder you’ll find subfolders for both graphics and sound assets, as well as the font we’ve used for the title screen and scores. To take pivaders for a test-drive, simply enter the pivaders subdirectory (cd pivaders/pivaders) and type:
Use the arrow keys to steer left and right and the space bar to shoot. You can quit to the main screen with the Esc key and press it again to exit the game completely.
Step 04 Animation & sound
Compared with the game from last month’s tutorial, you’ll see it’s now a much more dynamic project. The protagonist ship now leans into the turns as you change direction and corrects itself when you either press the opposite direction or lift your finger off the button. When you shoot an alien ship, it explodes with several frames of animation and should you take fire, a smaller explosion occurs on your ship. Music, lasers and explosion sound effects also accompany the animations as they happen.
Step 05 Finding images to animate
Before we can program anything, it’s wise to have assets set up in a way we can use them. As mentioned, we’ve opted to use sprite sheets; these can be found online or created with GIMP with a little practice. Essentially they’re a mosaic made up of individual ‘frames’ of equally sized and spaced images representing each frame. Find ready-made examples at OpenGameArt.org, as used here.
Step 06 Tweaking assets
While many of the assets on sites like OpenGameArt.org can be used as is, you may want to import them into an image-editing application like GIMP to configure them to suit your needs – as we did with our ship sheet asset to help us keep the code simple. We started with the central ship sprite and centred it into a new window. We set the size and width of the frame and then copy-pasted the other frames either side of it. We ended up with 11 frames of exactly the same size and width in a single document. Pixel-perfect precision on size and width is key, so we can just multiply it to find the next frame.
Step 07 Loading the sprite sheet
Since we’re inheriting from the Sprite class to create our Player class, we can easily alter how the player looks on screen by changing Player.image. First, we need to load our ship sprite sheet with pygame.image.load(). Since we made our sheet with a transparent background, we can append .convert_alpha() to the end of the line so the ship frames render correctly (without any background). We then use subsurface to set the initial Player.image to the middle ship sprite on the sheet. This is set by self.ani_pos, which has an initial value of 5. Changing this value will alter the ship image drawn to the screen: ‘0’ would draw it leaning fully left, ‘11’ fully to the right.
Step 08 Animation flags
Slightly further down the list in the initialising code for the Game class, we also set two flags for our player animation: self.animate_left and self.animate_right. As you’ll see in the Control method of our Game class, we use these to ‘flag’ when we want animations to happen using Boolean (True or False) values. It also allows us to ‘automatically’ animate the player sprite back to its natural resting state (otherwise the ship will continue to look as if it’s flying left when it has stopped).
Step 09 The animation method
These flags pop up again in the core animation code for the player: animate_player() within the Game class. Here we use nested if statements to control the animation and physically set the player image accordingly. Essentially it states that if the animate_right flag is True and if the current animation position is different to what we want, we incrementally increase the ani_pos variable and set the player’s image accordingly. The Else statement then animates the ship sprite back to its resting state and the same logic is then applied in the opposite direction.
Step 10 Animating explosions
The player_explosion() and alien_explosion() methods that come after the player animation block in the Game class are similar but simpler executions of essentially the same thing. As we only need to run through the same predefined set of frames (this time vertically), we only need to see if the self.explode and self.alien_explode flags are True before we increment the variables that change the image displayed. As the sprite sheet is vertical, the variables alien_explode_pos and explosion_image are set to a different part of subsurface than before.
Pygame makes it easy to add a musical score to a project. Just obtain a suitable piece of music in your preferred format (we found ours via freemusicarchive.org) and load it using the Mixer Pygame class. As it’s already been initialised via pygame.init(), we can go ahead and load the music with this code:
The music.play(-1) requests that the music should start with the app and continue to loop until it quits. If we replaced -1 with 5, the music would loop five times before ending. Learn more about the Mixer class.
Step 12 Using sound effects
Loading and using sounds is similar to how we do so for images in Pygame. First we load the sound effect using a simple assignment. For the laser beam, the initialisation looks like this:
self.bullet_fx = pygame.mixer.Sound( ‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’)
Then we simply trigger the sound effect at the appropriate time. In the case of the laser, we want it to play whenever we press the space bar to shoot, so we place it in the Game class’s Control method, straight after we raise the shoot_bullet flag.
If you’re struggling to find free and open sound effects, we recommend Freesound.org.