Hello World Revisited

As I mentioned in Chapter 1, there is a tradition when learning new languages that the first code you write displays the text "Hello, World!" on the screen. Technically we have already done this with a print ' Hello, World!' statement—but it is a little disappointing because as game programmers we are interested in creating appealing visuals and a line of text just does not cut it! We are going to create a Hello World script with Pygame that opens a graphical window on your desktop and draws an image under the standard mouse cursor. When run, you'll see a window similar to what's shown in Figure 3-1.

Figure 3-1. Hello World in Pygame

See Listing 3-1 for the code. Run it now if you like; we will go through it step by step in this chapter.

Listing 3-1. Hello World Redux (helloworld.py) #!/usr/bin/env python background_image_filename = 'sushiplate.jpg' mouse_image_filename = 'fugu.png'

import pygame from pygame.locals import * from sys import exit pygame.init()

screen = pygame.display.set_mode((640, 480), 0, 32) pygame.display.set_caption("Hello, World!")

background = pygame.image.load(background_image_filename).convert() mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()

while True:

for event in pygame.event.get(): if event.type == QUIT: exit()

screen.blit(background, (0,0))

x, y = pygame.mouse.get_pos() x-= mouse_cursor.get_width() / 2 y-= mouse_cursor.get_height() / 2 screen.blit(mouse_cursor, (x, y))

pygame.display.update()

We need two images for Listing 3-1: one to use as a background and another to draw as our mouse cursor. You can download the files for this and the other samples from the Source Code/ Download section at the Apress web site. If you don't have Internet access at the moment, you can use image files you have on your hard drive, or make them with any graphics- or photo-editing software. Any image is fine for the background, as long as it is at least 640 by 480 in size (any larger and the excess will be clipped). For the mouse cursor, you will need a smaller image that fits comfortably inside the background; a good size is 80 by 80. To continue with the fugu theme of the first chapter, the official background will be a picture of bowls and chopsticks, and a picture of a very raw fugu for the mouse cursor. The first two lines set the file names of the images; if you are using different images, you should replace the file names with the location of your images. Let's break this script into bite-sized chunks. At the top of the script we import the external modules, classes, functions, and so forth we will need when running the example:

import pygame from pygame.locals import * from sys import exit

The first line imports the pygame package, which gives us access to all of its submodules, such as pygame.image and pygame.sound. The second line imports a number of functions and constants (values that don't change) into the top-level namespace. It isn't essential to do this in order to use Pygame, but it is convenient because we don't have to precede frequently used values with the pygame namespace. The last import statement imports a single function from sys (a module in the standard library). As you may have guessed, the purpose of exit is to immediately finish with the script. Calling it will cause the Pygame window to disappear and Python to close. The script will call exit when the user clicks the close button; otherwise, the user would have no way of closing the window!

■Tip If you get into a situation where you can't close the Pygame window, you may be able to stop Python in its tracks by pressing Ctrl+C.

This rather simple line of Python code actually does a lot of work: pygame.init()

It initializes each of the submodules in the pygame package, which may load drivers and query hardware so that Pygame is ready to use all the devices on your computer. You can initialize only the modules you intend to use by calling the init function in each submodule individually; for example, pygame.sound.init() will initialize the sound module. This can make the script start a little quicker because only the modules you actually use will be initialized. For games you will require most, if not all, of the modules—so we will stick with this catchall initialize function. After we call it, we have the full power of Pygame at our disposal!

After initializing Pygame we need to create a display surface:

screen = pygame.display.set_mode((640, 480), 0, 32) pygame.display.set_caption("Hello, World!")

The display could be a window on your desktop or it could be the entire screen, but you always access it via a Pygame Surface object. The call to pygame.display.set_mode in our script returns the Surface object representing the window on your desktop. It takes three parameters; only the first is required, which should be a tuple containing the width and height of the display we want to create. Our window will be 640 x480 pixels, which is large enough so we can see what is happening, but not so large that it obscures too much of the desktop. The next parameter we give to set_mode is a value containing flags used in the display creation. A flag is a feature that can be switched on or off; you can combine several flags together with the bitwise OR operator (|). For instance, to create a double-buffered hardware surface, set the flags parameter to DOUBLEBUF| HWSURFACE. See Table 3-2 for the flags you can use. I will cover them in more detail in the "Opening a Display" section later in this chapter. We won't be enabling any of these flags for this first Pygame script, so the value we give for flags is just 0, which is also the default.

Table 3-2. Flags for pygame.display.set_mode

Flag

Purpose

FULLSCREEN

Creates a display that fills the entire screen.

DOUBLEBUF

Creates a "double-buffered" display. Recommended for HWSURFACE or OPENGL.

HWSURFACE

Creates a hardware-accelerated display (must be combined with the

FULLSCREEN flag).

OPENGL

Creates an OpenGL renderable display.

RESIZABLE

Creates a resizable display.

NOFRAME

Removes the border and title bar from the display.

The next parameter specifies the depth of the display surface, which is the amount of bits used to store colors in the display. A bit, or binary digit, is the most fundamental unit of storage in a computer. Bits have exactly two potential values, 1 or 0, and are arranged in memory as groups of 8. A group of 8 bits is called a byte. Don't worry if this sounds like techno-babble to you; Python tends to hide this kind of thing from the programmer. We will use the value 32 for our bit depth because it gives us the most colors; see Table 3-3 for other potential bit-depth values. If you don't supply a value for the depth or set it to 0, Pygame will use the depth of your desktop.

Table 3-3. Bit-Depth Values

Bit Depth

Number of Colors

8 bits

256 colors, chosen from a larger palette of colors

15 bits

32,768 colors, with a spare bit

16 bits

65,536 colors

24 bits

16.7 million colors

32 bits

16.7 million colors, with a spare 8 bits

* It is possible to have other bit depths, but these are the most common.

* It is possible to have other bit depths, but these are the most common.

■Note Sometimes Pygame is unable to give us the exact display we ask for. It may be that the graphics card doesn't support the features we are requesting. Fortunately, Pygame will choose a display that is compatible with the hardware and emulates the display we actually asked for. Thank you, Pygame!

If all goes well, the call to set_mode will display a Pygame window on your desktop and return a Surface object, which is then stored in the variable screen. The first thing we do with our newly created surface is call set_caption in the display module to set the title bar of the Pygame window. We set the title to "Hello, World!"—just to make it a valid Hello World script!

Next up we use the load function in pygame. image to load the two images for the background and mouse cursor. We pass in the file names of the images stored at the start of the script:

background = pygame.image.load(background_image_filename).convert() mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()

The load function reads a file from your hard drive and returns a surface containing the image data. These are the same type of objects as our display, but they represent images stored in memory and aren't visible until we draw them to the main display. The first call to pygame. image .load reads in the background image and then immediately calls convert, which is a member function for Surface objects. This function converts the image to the same format as our display, because it is faster to draw images if the display is of the same depth. The mouse cursor is loaded in a similar way, but we call convert_alpha rather than convert. This is because our mouse cursor image contains alpha information, which means that portions of the image could be translucent or completely invisible. Without alpha information in our mouse image, we are limited to an unsightly square or rectangle as our mouse cursor! The next chapter will cover alpha and image formats in more detail.

The next line in the script jumps straight into the main game loop:

while True:

This while loop has True as the condition, which means it will loop continually until we break out of it, or force it to exit in some other way. All games will have a loop similar to this, which typically repeats once per screen refresh.

Inside the main game loop we have another loop—the event loop, which most games will also have in one form or another:

for event in pygame.event.get(): if event.type == QUIT: exit()

An event is how Pygame informs you that something has happened outside your code. Events are created for many things, from key presses to receiving information from the Internet, and are queued up for you until you handle them. The function get in the pygame. event module returns any events waiting for us, which we then loop through in a for loop. For this script, we are only interested in the QUIT event, which is generated by Pygame when the user clicks the close button in the Pygame window. So if the event type is QUIT we call exit to shut down, and all other events are ignored. In a game, of course, we would have to handle a greater number of events.

The next line blits the background image to the screen (blitting means copying from one image to another):

screen.blit(background, (0,0))

This line uses the blit member function of the screen Surface object, which takes a source image—in this case, our 640 x480 background—and a tuple containing the destination position. The background will never move; we just want it to cover the entire Pygame window, so we blit to the coordinate (0, 0), which is the top left of the screen.

■Tip It is important that you blit to every portion of the screen. If you don't, strange visual effects may occur when you animate things, and your game may look different on each computer it is run on. Try commenting out the call to screen.blit to see what happens.

After we draw the background, we want to draw mouse_cursor underneath the usual mouse pointer:

x, y = pygame.mouse.get_pos() x -= mouse_cursor.get_width()/2 y -= mouse_cursor.get_height()/2 screen.blit(mouse_cursor, (x, y))

Getting the position of the mouse is nice and simple; the pygame.mouse module contains all we need to work with the mouse, including get_pos, which returns a tuple containing the mouse coordinates. The first line unpacks this tuple into two values for convenience: x and y. We could use these two values as coordinates when we blit the mouse cursor, but that would place the top-left corner of the image under the mouse, and we want the center of the image to be under the mouse. So we do a little math (fear not!) to adjust x and y so that the mouse image is moved up by half its height and left by half its width. Using these coordinates places the center of the image right under the mouse pointer, which looks better. At least it does for an image of a fish—if you want to use a more typical pointer image, adjust the coordinates so that the tip lies underneath the real mouse coordinates.

Blitting the mouse image is done in the same way as blitting the background, but we use the coordinates we calculated rather than (0, 0). This is enough to create the effect we are looking for, but there is one more thing we have to do before we can see anything:

pygame.display.update()

When you build an image through blits to the screen surface, you won't see them right away. This is because Pygame first builds up an image to a back buffer, which is an invisible display in memory, before it is displayed. If we didn't have this step, the user would see individual blits as they happen, which would flicker most unpleasantly. For games programmers, flicker is the enemy! We want to see silky-smooth, convincing animation. Fortunately a call to pygame.display. update() is all we need to ensure that the image we have created in memory is shown to the user without flicker.

When you run this script, you should see something like Figure 3-1. If you are using the "official" images, then an odd-looking fish will dutifully follow the mouse cursor.

Was this article helpful?

+1 0

Post a comment