Linux DevCenter    
 Published on Linux DevCenter (http://www.linuxdevcenter.com/)
 See this if you're having trouble printing code examples


Retro Gaming Hacks, Part 1: Clone Pong, Using Only SDL (and Your Brain)

by Josh Glover, contributor to Retro Gaming Hacks
12/15/2005

One of the great things about the games of yore is that they tended to be pretty simple, mostly because the hardware that pushed them only could do approximately 1.5 floating-point operations per day (FLOPD--an unfortunate acronym if ever I've seen one). Of course, one of the benefits of such spartan software is that in an actual game, you know, something "fun," has to exist at the kernel. When you don't have full-motion, cinema-quality cutscenes to distract the player, you had better serve up some serious gameplay. And the people working for Atari and Broderbund knew how to cook up games, including Atari's smash-hit video game Pong--one of the greatest games ever invented, and one of the simplest to implement.

So gather up your C programming skills, your dusty old eighth grade geometry book, and a copy of the Simple DirectMedia Layer (SDL)--we're going to show you how to clone Pong all by yourself. And stay tuned--this is the first of a three-part series. The next two articles (Adding Paddles to Your Pong and Add a Ball and Score to Your Pong) will build on what we do here, so look for those in the coming weeks.

SDL

As mentioned in the previous paragraph, SDL is the Simple DirectMedia Layer. If you are wondering how this has any relevance on the cloning of Pong, allow me to explain. In the heady, dot-com days of 1998, a company named Loki Entertainment Software was founded, with the mission of porting best-selling games to Linux. Their first port was of Civilization: Call to Power, which hit stores in April of 1999. Loki ported 19 other great games before they closed up shop at the end of January 2003.

As great as Loki's ports were, the crown jewel of its collection was not a game, but a game library: the Simple DirectMedia Layer. Loki, an open source company, released SDL under the GNU Lesser/Library Public License (LGPL), allowing anyone to download and use SDL to create his or her own games. SDL is written in C (and you can use it natively in C++ code), but bindings exist for Ada, Eiffel, Java, Lua, ML, Perl, PHP, Pike, Python, and Ruby. SDL does a great job of abstracting away the nasty details of game writing and letting you concentrate on the fun stuff, like writing your very own Pong clone! To install it, follow the directions for your Linux distribution:

Debian GNU/Linux

  1. Become root.
  2. Run the command:
  3. apt-get install libsdl1.2 libsdl-ttf1.2 libsdl1.2-dev \ 
     libsdl-ttf1.2-dev

Gentoo

  1. Become root.
  2. Run the command:
  3. emerge libsdl sdl-ttf

Red Hat Linux, Fedora, and Other RPM-Based Distributions

  1. Visit the SDL project's download page and save the RPMs for the latest Runtime and Development Libraries (Version 1.2.8-1, as of this writing) to your /tmp directory.
  2. Open a terminal window and become root, then run this command:
  3. rpm -Ivh /tmp/SDL-*.rpm

Other Linux/Unix Distributions

  1. Visit the SDL project's download page and save the latest source tarball (SDL-1.2.8.tar.gz, as of this writing) to your /tmp directory.
  2. Open a terminal window and become root, then run these commands:
  3. cd /tmp
    tar xvzf SDL-1.2.8.tar.gz && rm SDL-1.2.8.tar.gz
    cd SDL-1.2.8
    ./configure && make && make install

Other Operating Systems

Users of Windows, BeOS, and Mac OS (both Classic and X) can also visit the SDL project's download page, grab the appropriate distribution files for the latest Runtime and Development Libraries, and install them. You may find the "How to set up your SDL Build Environment" article on the Game Programming Wiki to be quite useful.

Note: Windows users can choose between Visual C++ and MingW32 versions of SDL. MingW32 is a free minimal Windows development environment that you can get using the Cygwin installer. If you choose the Cygwin route, it will closely resemble the Linux/Unix/Mac OS X-friendly instructions in the rest of this hack.

With SDL now installed, let's get on with the hacking of code.

A Rough Outline

As with any engineering project, a video game should start with a plan. We'll write our plan in C. Open up your favorite editor and create a new file. I recommend calling it sdl-pong.c.

Note: This hack assumes you are coding in a Unix, Linux, or Unix-like environment (Mac OS X, Cygwin, and so on). If you are not, you should know how to work your compiler, as when I say "gcc this, gcc that," you will have to translate to "File > Compile," and so on. For a pure hacking experience, install the GNU Compiler Collection (gcc) on your machine, and write your coding in a (programmer's) text editor, such as XEmacs or vi.

Retro Gaming Hacks

Related Reading

Retro Gaming Hacks
Tips & Tools for Playing the Classics
By Chris Kohler

Populate the file with the following "code" (which is basically all comments, but that's why we get to call it a "plan"), and save it as sdl-pong.c:

// Standard library headers
#include <stdio.h>

// SDL headers
#include <SDL.h>

// Macro definitions

// Structure definitions

// Function definitions


// Main program
int main( int argc, char **argv ) {

  // Declare variables

  // Initialise game data
  
  // Parse command-line arguments

  // Initialise SDL
  
  // Initialise sprite locations

  // Main loop
  while (1) {

    // (Re)draw the screen

    // Handle events (keyboard input, mainly)

    // Move sprites about
    
    // Give the CPU a break
    
  } // while (main loop)

  // Victory!
  return 0;

} // main()

If you do not program in C, you may want to look at a quick overview of the language--the University of Leicester's Computer Centre has a decent-looking C tutorial. I will try to explain the tricky bits as I go.

What we have written is an English-language description of how we plan to implement our game. But, as you may have noticed, we have also written a C program. One that does nothing useful, of course, but a C program nonetheless. To prove this (and demonstrate how to compile an SDL program at the same time), let's fire up gcc:

gcc -g -Wall -I/usr/include/SDL -o sdl-pong sdl-pong.c -lSDL

If you aren't familiar with gcc, a bit of explanation may be in order. The -g flag builds the program with symbols intact, which means that you will be able to use a debugger (such as the fabulous GNU Debugger, gdb) to fix any introduced code problems. The -Wall flag turns on all warnings; a good thing to do, as compiler warnings mean that the compiler cannot quite understand what you mean. The -I/usr/include/SDL bit tells gcc to add /usr/include/SDL to the list of directories that it searches for included files (that is, header files).

Note: It is possible that your Linux distribution or flavor of Unix decided to install the SDL header files somewhere other than /usr/include/SDL; you will know this in a hurry if gcc spews an error message like "SDL.h: No such file or directory" when you try to compile. If you know how, try to entice your package system to show you where it installed all of the files for the SDL package (Gentoo users, run qpkg -I -l libsdl; RPM types, try rpm -ql sdl). Failing this, you can always try find / -name SDL.h 2>/dev/null on a Unix system, or use the file finder tool in the Windows Start menu. However you find the SDL.h file, stick the directory in which it resides after a -I on the gcc command line and all will be well.

The -o sdl-pong lets gcc know that we want the compiled program to be named sdl-pong; leave the -o off the command line and your program is likely to be called a.out, which is not such a catchy or memorable name for a game. Unsurprisingly, sdl-pong.c is the name of the file containing the source code that we want to compile. Finally, the -lSDL flag tells gcc to link the libSDL.so shared library in with your program. Without the -lSDL flag, gcc would spew errors like: "undefined reference to `SDL_Init'."

Don't worry if you did not understand the last paragraph, you can just treat the gcc lines as mysterious incantations that result in you being able to play the game that you wrote.

And speaking of playing the game--to run your freshly compiled program, type:

./sdl-pong

The program will start up--and just hang. Right, that is what infinite loops tend to do: remember the while (1) { ... } block in the middle of the code, labeled "Main loop?" Don't worry, you can get your terminal back by hitting Ctrl-C, and you will put some code in that main loop soon.

Initializing SDL and Doing Something Useful at Long Last!

The first order of business for any SDL application is initializing SDL itself. To do this, simply add to your sdl-pong.c source code, under the "Initialise SDL" comment:

  if (SDL_Init( SDL_INIT_VIDEO ) != 0) {

    fprintf( stderr, "Could not initialise SDL: %s\n", SDL_GetError() );
    return 1;

  } // if (could not init video)

SDL_Init() is doing all the work--the other lines are just checking the return value and handling an error condition by returning from the main function, which causes the program to exit. Since this is Unix, a return value of 0 means no error; when you see a non-zero value returned, start worrying. Upon exit, sdl_pong returns 1, a non-zero value, to let the executing shell know that our program is exiting in response to an error.

Once you have initialized SDL, you need to make sure that you tear it down cleanly before exiting the program for any reason. For this purpose, define a new function:

// Function definitions
int cleanUp( int err );

The implementation of the function is simple: all you need to do is call the SDL_Quit() function with no parameters, but the cleanUp() function takes and returns an integer so that you can make sure the exit code is reported to whoever executed the program. Add the cleanUp() function to the end of your file, after main():

/* Function: cleanUp()
 *
 * Cleanly tears down SDL in preparation for an exit.
 *
 * Parameters:
 *
 *   err - Unix-style error code
 *
 * Returns:
 *
 *   err parameter
 */

int cleanUp( int err ) {

  SDL_Quit();

  return err;

} // cleanUp()

Note that we kindly write a header comment for this function; this is so that other people who might want to modify our program know what the function does and how to call it. It is also useful when you forget how a function works because you wrote it a long time ago (and in my case, "a long time ago" means more than an hour ago). The specific style of this header comment can be parsed by a program called NaturalDocs to generate lovely HTML versions of your API documentation.

Now that you have the cleanUp() function, you will want to modify the last line of the main() function by changing:

  # Victory!
  return 0;

to read:

  # Victory!
  return cleanUp( 0 );

This ensures that SDL releases all of the memory and hardware resources that it grabbed on the program's behalf before exiting.

The next step is to display the main window in which all of the action will take place. Let's make it 640 pixels wide and 480 pixels tall for now, but we'll define the screen width and height as macros so that we can change them later. Add this to your sdl-pong.c source code:

// Macro definitions
#define SCREEN_WIDTH  640 // width of the screen, in pixels
#define SCREEN_HEIGHT 480 // height of the screen, in pixels

The main window will be represented by a variable of the SDL_Surface type. Add this to the "Declare variables" section of the code:

  SDL_Surface *screen; // main game window

To display the window, call the SDL_SetVideoMode() function (add the following to the section of the code initializing SDL):

  if ((screen =
       SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 8, SDL_SWSURFACE ))
      == NULL) {

    fprintf( stderr, "Could not set SDL video mode: %s\n", SDL_GetError() );
    return cleanUp( 1 );
    
  } // if (could not set mode)

The first two parameters to SDL_SetVideoMode() are the width and height of the window, respectively. The third parameter is bits per pixel--the number of bits SDL will use to represent the color of every pixel on the screen. Obviously, the higher the number, the wider the variety of colors that can be displayed. SDL supports eight, 15, 16, 24, and 32 bits per pixel. We can get away with a paltry eight bits (one byte) per pixel because this Pong clone is going to be monochromatic. Don't worry, you may play Ted Turner and colorize the game later if you choose (details on this and other improvements to the game will come in the next two articles in this series). The final parameter is for SDL flags: we only need SDL_SWSURFACE, but for a list of all of the flags that can be set, check out the entry for SDL_SetVideoMode() in the SDL library documentation. Note that these flags are bitmasks, so to set multiple flags, you will need to OR them together, like so: SDL_SWSURFACE | SDL_FULLSCREEN.

Note again to check the return value of the function for errors. In this case, since SDL_SetVideoMode() returns a pointer (if you do not know what a pointer is, don't worry about it just yet, but be warned that the concept of pointers will haunt your waking hours if you ever decide to use C for anything complicated), a value of zero (NULL is simply a #define 0 in some C standard library header file) signals an error, and anything else is OK. To recap, a return value of 0 in Unix always means everything is fine, except when it doesn't.

Many tutorials tend to leave error checking code out, as it clutters the presentation of the code. I left the error checking in, because many headaches can be avoided by knowing what went wrong where.

The code we have written thus far will display a game window, then loop forever (or until the mailed fist of SIGINT comes crashing down, prompted by a judicious Ctrl-C). This is progress, but it is still pretty boring from the perspective of a potential player (my perspective is that of a potential poet, as you can see from my abuse of alliteration). Let's add some window dressing (no pun intended, until I thought about it and realized that it was an extremely clever pun) in the form of a title bar caption and hiding the mouse cursor when over our window. Add this just beneath the SDL_SetVideoMode() bit:

  // Set window caption and turn off the mouse cursor
  SDL_WM_SetCaption( "SDL Pong", "SDL Pong" );
  SDL_ShowCursor( SDL_DISABLE );

Finally, let's provide users of the game with a way to exit (though why they might want such a facility is beyond me). We will accomplish this by adding an event handling loop inside the main loop. As most games are event-driven (a controller movement is an event; a packet arriving from the network may trigger an event; an external windowing system action may trigger an event; and so on), SDL will happily collect events on your behalf and queue them up, waiting for you to deal with them. To do just that, call SDL_PollEvent(), which grabs the first event SDL has for us. But first, we will need a variable of the SDL_Event type to pass as the sole parameter to SDL_PollEvent(). Right before the declaration of the SDL_Surface variable, add the line shown in bold:

  // Declare variables
  SDL_Event    event;  // SDL events
  SDL_Surface *screen; // main game window

and inside the main loop, add the lines shown in bold:

  // Main loop
  while (1) {

    // (Re)draw the screen

    // Handle events (keyboard input, mainly)
    while (SDL_PollEvent( &event )) {

      if (event.type == SDL_QUIT ||
          (event.type == SDL_KEYDOWN &&
           event.key.keysym.sym == SDLK_ESCAPE))
        return cleanUp( 0 );

    } // while (handling events)

    // Move sprites about

This handles two events, namely SDL_QUIT, which is generated when the user quits the program externally (usually by simply clicking on the X in the main window's title bar), and the SDL_KEYDOWN event, which is generated every time a key on the keyboard is pressed (in case you were wondering, yes, an SDL_KEYUP event occurs when said key, or any key for that matter, is released). We only care about the Esc key, of course, which is the de facto standard for quitting SDL games. That is what the event.key.keysym.sym == SDLK_ESCAPE code is all about; for a complete list of key symbolic names (keysyms), check out the SDLKey page on the SDL library documentation site.

Once the program catches an event that tells it to quit, it does so simply by returning the return value of cleanUp() with an error code of 0 to indicate a normal exit. Get used to this idiom, as you will see it again when we get to parsing command-line arguments in the next two articles.

The last thing to be done before compiling and testing this iteration of SDL Pong is adding a small delay at the end of the main loop. For now, this is just to be kind to the CPU and let some other task run, but a little while later, it will control the speed of our game. To wait for a bit, we use the SDL_Delay() function, which takes as its only parameter the number of milliseconds to wait. Add this code right before the end of the main loop:

    // Give the CPU a break
    SDL_Delay( 30 );

Thirty milliseconds (0.03 seconds) may not seem like long to you, but to a modern CPU, it is time enough to read the paper and walk the dog.

OK, that's it! Your "game" now at least does something that is perceivable to the naked eye of a non-coder (such as your grandmother--unless she happens to be, say, Admiral Grace Hopper, in which case she could code you under the table any day!). Run gcc again to re-compile it (don't forget this step after every change to your code, or you might spend a lot of time wondering why your latest bug fix did not work):

gcc -g -Wall -I/usr/include/SDL -o sdl-pong sdl-pong.c -lSDL

and then run the game:

./sdl-pong

Editor's note: Next week, Josh will cover how to add paddles to your Pong game, and in the first week of the new year, he'll cover how to add a ball and score.

Josh Glover has been hacking code for as long as anyone can remember. He is employed as a Unix systems administrator by Amazon.co.jp.


Return to the Linux DevCenter.

Copyright © 2009 O'Reilly Media, Inc.