Learn the basics of computer animation before creating your own masterpiece.
If you’ve made it this far in the book, you’ve probably considered the ultimate gaming hack at some point: writing your own game. Maybe you have a story best told through interactive fiction ( [Hack #85] ). Maybe not, though; some stories need more pictures than words.
Though the systems emulated in Chapter 1 all have fantastic limitations, creating similar games today is much, much easier. Faster computers, higher-level languages, and well-documented, reusable libraries make it easy to learn game programming. It’s still difficult to create a high-powered 3D shooter engine, but with a little practice, you can write a fun little 2D game in a weekend.
The easiest place to start is by putting graphics on the screen.
There are dozens of programming languages that can produce games. We’ll explore Python (http://www.python.org/), an open source language that’s easy to learn, powerful, and used in many professional game-development studios. It also has the advantage of working with the excellent PyGame (http://www.pygame.org/) game-programming library.
If you’re using a Unix or Mac OS X system, you likely have Python installed already. If you’re using Windows, you’ll probably need to install it yourself. At the time of writing, the current stable release is Python 2.3.3. Download an installer for Windows or Mac OS X from the Python download page (http://www.python.org/download/).
You’ll also need PyGame, which depends on the SDL libraries (http://www.libsdl.org/).
Windows users: download a PyGame installer from the PyGame download
page (http://www.pygame.org/download.shtml). Make
sure the version you download matches the version of Python you have
installed. You’ll probably want
pygame-1.6.win32-py2.3.exe. Fortunately, this
includes the SDL libraries. Be sure to install the PyGame
documentation too; it’s a separate download.
Mac OS X users: install the MacPython for Panther add-ons (http://homepages.cwi.nl/~jack/macpython/download.html), then launch the MacPython package manager, choose Open URL, and enter:
http://undefined.org/python/pimp/darwin-7.2.0-Power_Macintosh.plistFrom here, you can install PyGame and its dependencies.
I assume that Unix users already know how to compile from source or can find packages for their distributions.
There’s one secret to computer animation: there’s no movement. Like movies and TV, computers achieve the appearance of animation by drawing many static images with very small changes. Once you understand this, you’re well on your way to creating animations.
Let’s start out very small, drawing a blue square on the screen with Python and PyGame.
PyGame and SDL drawings work on surfaces. The main screen you see is a surface. Any image you load is a surface. Most of the bare bones of your animations will mean drawing onto a surface, copying from one surface to another, and updating the main surface (that is, copying data from memory to the graphics card). The most important thing to do in any PyGame application is to create the main screen surface:
#!/usr/bin/python import pygame import time pygame.init( ) screen = pygame.display.set_mode(( 640, 480 ))
The first line tells the shell to use the python
program in /usr/bin to run the program (at least
on Unix machines; Windows machines will likely ignore it). The second
line loads the PyGame library, making its features available to the
rest of the program. The third line loads a library of time-related
functions.
The real work starts in the fourth and fifth lines. Line four initializes PyGame and all its supporting modules. Line five actually creates the main window of 640 480 pixels.
The double parentheses are important. The method set_mode() itself needs a set of parenthesis to group its arguments.
The second set of parenthesis groups the width and height values into
a single unit—a list of two values.
This isn’t very interesting yet, but it’s a start.
Once you know how to create surfaces, you need some way to update them. It’s important to be able to update only part of a surface.[20] SDL and PyGame use rectangles, which have X and Y coordinates and widths and heights. If you want to draw a blue square to the screen, you need a square rectangle:
blue_rect = pygame.Rect(( 270, 190 ), ( 100, 100 ))
This creates a new Rect object (what PyGame calls
rectangles). Its initialization takes two lists, a set of
coordinates, and its height and width. This rectangle is not attached
to any surface, yet.
How do you go from a rectangle to a surface? You have a couple of options. Let’s change the background of the main screen from black to white and then draw the blue square:
background = pygame.Surface( screen.get_size( ) ) background.fill(( 255, 255, 255 )) background.fill(( 0, 0, 255 ), blue_rect ) screen.blit( background, background.get_rect( ) )
Drawing order matters! There’s no point in drawing the square first if you’ll immediately paint over it with the background.
The first line creates a new surface the same size as the main screen. This will be the background. Now you have to fill it in with the appropriate information. The second line fills the background with the color white. PyGame colors use the RGB (red, green, blue) system of integer values in which 0 represents none of that color and 255 represents the maximum amount of that color. Full red plus full blue plus full green equals white (in an additive color system, anyway). With no destination rectangle specified, this fills the entire background surface.
The third line fills in the area represented by the rectangle created earlier with the color blue. This 100 100 pixel square is centered on the screen.
Finally, the fourth line blits, or copies image data from, the background to the main screen. This still doesn’t display anything, though. There’s one more step:
pygame.display.flip( ) time.sleep( 2 )
This code tells PyGame to send the updated screen data to the video card. Presto, a white screen with a blue square appears. The second line pauses the program for two seconds so you can admire your work.
Filling in rectangles is all well and good, but drawing static images isn’t even as interesting as Pong! Fortunately, working with actual image files is almost as easy as working with raw, boring surfaces. Let’s load a robot image I just happen to have lying around:
robot = pygame.image.load( 'robot.png' ).convert_alpha( )
Given a filename, this loads the image and converts the alpha channel information to work with the capabilities of the main screen. This is immaterial in a small example, but very important in other situations where parts of an image are transparent.
With the robot image loaded into a surface, you can draw it on the background in the same way you drew the background on the main surface:
background = pygame.Surface( screen.get_size( ) ) background.fill(( 255, 255, 255 )) background.blit( robot, robot.get_rect( ) )
Once again, the first line creates a background surface of the appropriate size, and the second fills it with the color white. The third line blits the robot onto the background. Change these lines in the above program to see a robot appear in the upper-left corner.
Why the upper-left corner? The robot’s rectangle’s X and Y coordinates are both 0, which corresponds to the upper-left corner of the screen. Remember, the blue square had X and Y coordinates of 270 and 190.
Hmm, the robot’s rectangle’s coordinates govern where it appears....
Remember, the trick to animation is drawing slightly different images to the screen in rapid succession. By looping around the code that draws the background and the robot and changing the position of the robot slightly, you can make this little guy zip across the screen. This code is a bit different from the previous version, so here’s the complete listing:
#!/usr/bin/python
import pygame
import time
pygame.init( )
screen = pygame.display.set_mode(( 640, 480 ))
robot = pygame.image.load( 'robot.png' ).convert_alpha( )
background = pygame.Surface( screen.get_size( ) )
robot_rect = robot.get_rect( )
for x in range( 0, 620 ):
robot_rect.x = x
background.fill(( 255, 255, 255 ))
background.blit( robot, robot_rect )
screen.blit( background, background.get_rect( ) )
pygame.display.flip( )
time.sleep( 2 )The big differences here are the robot_rect
variable and the loop. You first fetch the rectangle representing the
robot—including its X and Y coordinates of 0, 0 and, more
importantly, its height and width—into
robot_rect. Next, loop through a range of X
coordinates, and update the robot’s position by
setting the X coordinate in robot_rect. Finally,
paint the background white, blit the robot onto the background at the
updated position, and update the screen.
That’s not bad for about 20 lines of code.
Animation is a good place to start, but it’s not the place to stop. First, add user input to your motions ( [Hack #92] ). Then, make your work into a full game ( [Hack #93] ).
Python’s not the only good language in town. SDL works with several other languages, including C, C++, Perl, Ruby, and even Parrot. With all these languages, you can write solid, usable games. PyGame has a good mix of maturity and ease of use that make it particularly worth exploring. For more information, see the following sites:
The Python homepage at http://www.python.org/, especially its documentation (http://www.python.org/doc/2.3.3/)
The PyGame Documentation at http://pygame.org/docs/index.html
The SDL homepage at http://www.libsdl.org/, especially its tutorials and FAQs
The Perl SDL homepage at http://sdl.perl.org/
The Ruby SDL homepage at http://raa.ruby-lang.org/project/ruby-sdl/
The Parrot SDL homepage at http://wgz.org/chromatic/parrot/sdl/