Program Arcade Games
With Python And Pygame

Chapter 11: Bitmapped Graphics and Sound

To move beyond the simplistic shapes offered by drawing circles and rectangles, our programs need the ability to work with bitmapped graphics. Bitmapped graphics can be photos or images created and saved from a drawing program.

But graphics aren't enough. Games need sound too! This chapter shows how to put graphics and sound in your game.

11.1 Storing The Program in a Folder

Video: Organizing a project in a folder

The programs we've made so far only involve one file. Now that we are including images and sounds, there are more files that are part of our program. It is easy to get these files mixed up with other programs we are making. The way to keep everything neat and separated out is to put each of these programs into its own folder. Before beginning any project like this, click the “new folder” button and use that new folder as a spot to put all the new files as shown in Figure 11.1.

fig.create_a_folder
Figure 11.1: Creating a new folder

11.2 Setting a Background Image

Video: Organizing a project in a folder

Need to set a background image for your game? Find an image like Figure 11.2. If you are looking on-line in a web browser, you can usually right-click on an image, and save it onto the computer. Save the image to the folder that we just created for our game.

fig.background_image
Figure 11.2: Background Image

Any bitmap images used in a game should already be sized for how it should appear on the screen. Don't take a 5000x5000 pixel image from a high-resolution camera and then try to load it into a window only 800x600. Use a graphics program (even MS Paint will work) and resize/crop the image before using it in your Python program.

Loading an image is a simple process and involves only one line of code. There is a lot going on in that one line of code, so the explanation of the line will be broken into three parts. The first version of the our load command will load a file called saturn_family1.jpg. This file must be located in the same directory that the python program is in, or the computer will not find it:

# Image from http://ProgramArcadeGames
pygame.image.load("saturn_family1.jpg")

Be careful about using copyrighted images. If you publish a game with images that you don't have the rights to, that's illegal. If you are creating your own work that you aren't sharing, then it is ok. If you are doing a class assignment, make sure that you check with the instructor for his or her rules around it.

For class assignments, I suggest my students add a comment to the program just before loading the image. List where the image came from as we did in the example above. (Don't list Google as the image source, that's like listing the “library” as your reference source. Find the website the image came from.)

That code may load the image, but we have no way to reference that image and display it! We need a variable set equal to what the load() command returns. In the next version of our load command, we create a new variable named background_image. See below for version two:

background_image = pygame.image.load("saturn_family1.jpg")

Finally, the image needs to be converted to a format Pygame can more easily work with. To do that, we append .convert() to the command to call the convert function. The function .convert() is a method in the Image class. We'll talk more about classes, objects, and methods in Chapter 12.

All images should be loaded using code similar to the line below. Just change the variable name and file name as needed.

background_image = pygame.image.load("saturn_family1.jpg").convert()

Loading the image should be done before the main program loop. While it would be possible to load it in the main program loop, this would cause the program to fetch the image from the disk twenty or so times per second. This is completely unnecessary. It is only necessary to do it once at program start-up.

To display the image use the blit command. This “blits” the image bits to the screen. We've already used this command once before when displaying text onto a game window back in Chapter 5.

The blit command is a method in the screen variable, so we need to start our command by screen.blit. Next, we need to pass the image to blit, and where to blit it. This command should be done inside the loop so the image gets drawn each frame. See below:

screen.blit(background_image, [0, 0])

This code blit's the image held in background_image to the screen starting at (0, 0).

11.3 Moving an Image

Video: Moving an Image

Now we want to load an image and move it around the screen. We will start off with a simple orange space ship. You can get this and many other great assets from http://kenney.nl/. See Figure 11.3. The image for the ship can be downloaded from the book's website, or you can find a .gif or .png that you like with a white or black background. Don't use a .jpg.

fig.player_image
Figure 11.3: Player image

To load the image we need the same type of command that we used with the background image. In this case, I'm assuming the file is saved as player.png.

player_image = pygame.image.load("player.png").convert()

Inside the main program loop, the mouse coordinates are retrieved, and passed to another blit function as the coordinates to draw the image:

# Get the current mouse position. This returns the position
# as a list of two numbers.
player_position = pygame.mouse.get_pos()
x = player_position[0]
y = player_position[1]

# Copy image to screen:
screen.blit(player_image, [x, y])

This demonstrates a problem. The image is a space ship with a solid black background. So when the image is drawn the program shows Figure 11.4.

fig.nontrans_background

We only want the space ship, not a rectangular background! But all images we can load are rectangles, so how do we show only the part of the image we want? The way to get around this is to tell the program to make one color “transparent” and not display. This can be done immediately after loading. The following makes the color black (assuming BLACK is already defined as a variable) transparent:

player_image.set_colorkey(BLACK)

This will work for most files ending in .gif and .png. This does not work well for most .jpg files. The jpeg image format is great for holding photographs, but it does subtly change the image as part of the algorithm that makes the image smaller. Images in .gif and .png are also compressed, but the algorithms used in those formats do not change the image. The format .bmp isn't compressed at all, and results in huge files. Because the .jpg format changes the format, this means that not all of the background color will be the exactly the same. In Figure 11.5 the space ship has been saved as a jpeg with a white background. The white around the ship is not exactly (255, 255, 255), but just really close.

fig.jpeg_artifacts
Figure 11.5: JPEG Compression Artifacts

If you are picking out an image that will be transparent, choose a .gif or .png. These are the best formats for graphic art type of images. Photos should be .jpg. Keep in mind it is not possible to change a .jpg to another format just by renaming the file extension to .png. It is still a .jpg even if you call it something different. It requires conversion in a graphics program to change it to a different format. But once in a .jpg format, it has been altered and converting it to a .png won't fix those alterations.

Here are three great places to find free images to use in your program:
Kenney.nl
OpenGameArt.org
HasGraphics.com

11.4 Sounds

Video: Sounds

In this section we'll play a laser sound when the mouse button is clicked. This sound originally came from Kenney.nl. You can download and save the sound here:
ProgramArcadeGames.com/python_examples/en/laser5.ogg

Like images, sounds must be loaded before they are used. This should be done once sometime before the main program loop. The following command loads a sound file and creates a variable named click_sound to reference it:

click_sound = pygame.mixer.Sound("laser5.ogg")

We can play the sound by using the following command:

click_sound.play()

But where do we put this command? If we put it in the main program loop it will play it twenty times or so per second. Really annoying. We need a “trigger.” Some action occurs, then we play the sound. For example this sound can be played when the user hits the mouse button with the following code:

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        done = True
    elif event.type == pygame.MOUSEBUTTONDOWN:
        click_sound.play()

Uncompressed sound files usually end in .wav. These files are larger than other formats because no algorithm has been run on them to make them smaller. There is also the ever popular .mp3 format, although that format has patents that can make it undesirable for certain applications. Another format that is free to use is the OGG Vorbis format that ends in .ogg.

Pygame does not play all .wav files that can be found on the Internet. If you have a file that isn't working, you can try using the program Audacity to convert it to an ogg-vorbis type of sound file that ends in .ogg. This file format is small and reliable for use with pygame.

If you want background music to play in your program, then check out the on-line example section for:
ProgramArcadeGames.com/en/python_examples/f.php?file=background_music.py

Please note that you can't redistribute copyrighted music with your program. Even if you make a video of your program with copyrighted music in the background YouTube and similar video sights will flag you for copyright violation.

Great places to find free sounds to use in your program:
OpenGameArt.org
www.freesound.org

11.5 Full Listing

"""
 Sample Python/Pygame Programs
 Simpson College Computer Science
 http://programarcadegames.com/
 http://simpson.edu/computer-science/

 Explanation video: http://youtu.be/4YqIKncMJNs
 Explanation video: http://youtu.be/ONAK8VZIcI4
 Explanation video: http://youtu.be/_6c4o41BIms
"""

import pygame

# Define some colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Call this function so the Pygame library can initialize itself
pygame.init()

# Create an 800x600 sized screen
screen = pygame.display.set_mode([800, 600])

# This sets the name of the window
pygame.display.set_caption('CMSC 150 is cool')

clock = pygame.time.Clock()

# Before the loop, load the sounds:
click_sound = pygame.mixer.Sound("laser5.ogg")

# Set positions of graphics
background_position = [0, 0]

# Load and set up graphics.
background_image = pygame.image.load("saturn_family1.jpg").convert()
player_image = pygame.image.load("playerShip1_orange.png").convert()
player_image.set_colorkey(BLACK)

done = False

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.MOUSEBUTTONDOWN:
            click_sound.play()

    # Copy image to screen:
    screen.blit(background_image, background_position)

    # Get the current mouse position. This returns the position
    # as a list of two numbers.
    player_position = pygame.mouse.get_pos()
    x = player_position[0]
    y = player_position[1]

    # Copy image to screen:
    screen.blit(player_image, [x, y])

    pygame.display.flip()

    clock.tick(60)

pygame.quit()

11.6 Review

11.6.1 Multiple Choice Quiz

Click here for a multiple-choice quiz.

11.6.2 Short Answer Worksheet

Click here for the chapter worksheet.

11.6.3 Lab

Click here for the chapter lab.


You are not logged in. Log in here and track your progress.