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

## Retro Gaming Hacks, Part 3: Add a Ball and Score to Pong

by Josh Glover, contributor to Retro Gaming Hacks
01/05/2006

Now that we have moving paddles for our Pong clone, the only thing standing in the way of having some real fun is the fact that the ball does not in fact move. But this is a problem easily solved once you rediscover some basic geometry.

The first geometric law we will require is: slope equals rise over run. The ball is going to move in a straight line, so all we have to do to track its movement is, for every iteration of the main loop, change the y coordinate by the rise (or numerator) of the slope, and the x coordinate by the run (or denominator). The fact that the slope, a fractional sort of number, can be easily represented by two components is a big win for our integer-loving CPU. You can start by defining two more macros at the top of the sdl-pong.c file, where all of the macros live:

``````#define BALL_SPEED   6 // total change in x and y in the slope
#define SLOPE_MAX_DY 4 // the maximum change-in-y allowed in the slope``````

The macros will make a bit more sense once we add another structure definition (right above the `GameData` structure in the code):

``````// Structure definitions
typedef struct {

int dx;
int dy;

} Slope;``````

and a few new members to the `GameData` structure itself:

``````  int game_speed;   // game speed
int ball_speed;   // number of pixels the ball can move at once
int slope_max_dy; // maximum value for change-in-y component of the slope

SDL_Rect ball;  // ball
Slope    slope; // slope of the line representing the ball's path``````

Just as the game uses the `GameData` structure to hold a collection of related data, so will it use the `Slope` structure to keep track of all of the data necessary to keep track of the ball's movement: namely the change-in-x (run) and change-in-y (rise) components of the slope of the line along which the ball is currently moving. As always, you will need to initialize the new members of the `GameData` structure:

``````  // Initialise game data
game.running      = 1;
game.game_speed   = GAME_SPEED;
game.ball_speed   = BALL_SPEED;
game.slope_max_dy = SLOPE_MAX_DY;``````

You may be wondering if I forgot to initialize the `Slope`. Actually, I didn't; I just cannot do it in such a straightforward fashion because I want to introduce the one ingredient that makes a good game great: the element of chance. Why not randomly generate the starting `Slope` (within reason, of course)? You need to include two new headers at the top of the file:

``````// Standard library headers
#include <stdio.h>
#include <stdlib.h>
#include <time.h>``````

`stdlib.h` provides the `srand()` and `rand()` functions, and `time.h` provides the eponymous `time()` function, which you will need in the new `genSlope()` function. Add the definition of `genSlope()` between `cleanUp()` and `movePaddle()`, like so:

``void genSlope( GameData *game );``

and add the implementation between that of the same functions:

``````/* Function: genSlope()
*
* Randomly generates the slope of the vector of ball's travel.
*
* Parameters:
*
*   *game - game data
*/

void genSlope( GameData *game ) {

// Seed the random number generator with the current Unix timestamp
srand( time( NULL ) );

// Generate the change-in-y component of the slope randomly
game->slope.dy =
1 + (int)((float)game->slope_max_dy * rand() / (RAND_MAX + 1.0));

// The change-in-y component of the slope is
// whatever is left in the "budget"
game->slope.dx = game->ball_speed - game->slope.dy;

// Flip a coin for x and y directions
if ((int)(2.0 * rand() / (RAND_MAX + 1.0)))
game->slope.dx *= -1;
if ((int)(2.0 * rand() / (RAND_MAX + 1.0)))
game->slope.dy *= -1;

} // genSlope()``````
 Related Reading Retro Gaming Hacks Tips & Tools for Playing the Classics By Chris Kohler

Before `genSlope()` can generate any random numbers, it must seed the random number generator. If it does not, the first call to `rand()` will automatically seed the random number generator with 1, which seems OK, except for the fact that you will get the same sequence of "random" numbers every time you start the game. It does not take much thinking about this to see why that would defeat the entire purpose behind randomly generating the slope in the first place. So seed the random number generator we must, and what better to seed it with than the current timestamp? (To answer that question, a few bytes from /dev/random would be better to seed it with, but we will refrain in the interest of portability, as /dev/random does not exist on all systems.) The `time()` function helpfully returns the current Unix epoch time (which is the number of seconds that have elapsed since January 1, 1970), so you can just feed it directly into the gaping maw of the `srand()` function, as shown in this excerpt from `genSlope()`:

``````  // Seed the random number generator with the current Unix timestamp
srand( time( NULL ) );``````

Now that the diabolical hunger of the random number generator is sated, you can use it to generate the change-in-y portion of the slope:

``````  // Generate the change-in-y component of the slope randomly
slope->dy = 1 + (game->slope_max_dy * rand() / (RAND_MAX + 1.0));``````

This is a lot more complicated than it should be, as `rand()` likes to generate numbers between 0 and the C-library-defined macro `RAND_MAX`, which is typically a large number like 32,767. What you really want is an integer between 1 and the maximum allowable value for `dy`, so you must divide the value returned by `rand()` by `RAND_MAX` plus 1 to get a floating point number between 0 and 1, then multiply that by the aforementioned `slope_max_dy`, and finally, add 1 to the whole bloody thing. Luckily, computing the change-in-x part of the slope is much easier:

``````  // The change-in-x component of the slope is
// whatever is left in the "budget"
slope->dx = game->ball_speed - slope->dy;``````

Things should start to become clear now: we want the ball to move six pixels a turn, and we will generate the number of pixels it moves in the y direction randomly, and use the rest for x-centric movement (pun intended, sorry about that). And finally, to make sure the ball does not always move down and to the right from the start, have `genSlope()` randomly flip the sign on one or both components of the slope:

``````  // Flip a coin for x and y directions
if ((int)(2.0 * rand() / (RAND_MAX + 1.0)))
slope->dx *= -1;
if ((int)(2.0 * rand() / (RAND_MAX + 1.0)))
slope->dy *= -1;``````

(At this point, it becomes obvious that the "slope" is not truly a slope, because in the realm of SDL Pong, a slope of -1/-1 is not the same as a slope of 1/1; that's OK, however.)

Now all you have to do is make sure the `genSlope()` function gets called at some point. Right after the call to `resetSprites()`, but before we enter the black hole that is the main loop, would seem a logical choice:

``````  // Initialise our sprite locations
resetSprites( &game, 0 );

// Randomly generate the starting slope
genSlope( &game );

// Main loop
while (1) {``````

At this point, however, you have nothing to show for all of your hard work on slope-related issues, as the ball still does not move.

Adding a call to the `moveBall()` function right before the delay and the end of the main loop should do the trick:

``````    // Move the ball
moveBall( &game );

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

} // while (main loop)``````

That is, it should do the trick once you add a function definition to the top of the file:

``````// Function definitions
int  cleanUp( int err );
void genSlope( GameData *game );
void moveBall( GameData *game );
void movePaddle( GameData *game, int player, int dir );
void resetSprites( GameData *game, int erase );``````

and then implement the `moveBall()` function:

``````/* Function: moveBall()
*
* Moves the ball.
*
* Parameters:
*
*   *game - game data
*/

void moveBall( GameData *game ) {

// Erase the current ball
SDL_FillRect( game->screen, &(game->ball), game->black );
game->rects[game->num_rects++] = game->ball;

// Move the ball (reset height and width, as going off the screen seems
// to compress them)
game->ball.x += game->slope.dx;
game->ball.y += game->slope.dy;
game->ball.w  = BALL_W;
game->ball.h  = BALL_H;

// If the ball hits the top or bottom wall, bounce it
if (game->ball.y <= 0 || game->ball.y >= (SCREEN_HEIGHT - game->ball.h)) {

// Add a sound effect here?

// According to my grade eight geometry class, "the angle of refraction
// equals the angle of incidence" (thanks, Mrs. Lott!), so let's just
// multiply the y component of our slope by -1 to change its sign
game->slope.dy *= -1;

} // if (ball bouncing off top or bottom wall)

// If the ball has hit a player's paddle, bounce it
if (((game->ball.x <= game->p1.w) &&
(game->ball.y >= game->p1.y &&
((game->ball.y + game->ball.h) <= game->p1.y + game->p1.h))) ||
(game->ball.x >= (SCREEN_WIDTH - (game->p2.w + (game->ball.w))) &&
(game->ball.y >= game->p2.y &&
((game->ball.y + game->ball.h) <= game->p2.y + game->p2.h)))) {

// Add a sound effect here?

// Multiply the x component of our slope by -1 to change its sign; see
// note above on elementary geometry
game->slope.dx *= -1;

} // if (bouncing off paddle)

// If the ball hits the left or right wall, score a point for the
// appropriate player and return the ball to the centre
else if (game->ball.x < 0 || game->ball.x > (SCREEN_WIDTH - game->ball.w)) {

// Return the paddles and ball to their starting positions
resetSprites( game, 1 );

// Generate a new slope
genSlope( game );

} // else if (score!)

SDL_FillRect( game->screen, &(game->ball), game->white );
game->rects[game->num_rects++] = game->ball;

} // moveBall()``````

The beginning of the function is trivial stuff to us by now: erase the ball by filling the rectangle corresponding to its current location with the background color, then adding the change-in-x slope component to the ball's x coordinate and the change-in-y to its y coordinate. (We reset the width and height of the ball here because going off of the screen seems to squash the ball in some cases, so better safe than sorry.)

It is when the ball hits the top or bottom of the screen (and you know this by the y coordinate--if it is less than or equal to 0, it hits the top, and greater than or equal to `SCREEN_HEIGHT` minus ball height, it is the bottom--just as the paddles worked) that we will whip out the second law of geometry: the angle of refraction equals the angle of incidence. This is easy, because all you need to do is flip the sign on the `dy` component of the slope, which keeps the angle the same, just reverses the direction.

You must do the same thing, except to `dx`, when the ball collides with a player's paddle. Instead of trying to explain in prose the complicated conditional conundrum that I use for detecting such a collision, I invite you to turn once again to your trusty scratch paper, and draw the requisite shapes. All should be made clear, including the bug in my collision detection algorithm that I dubbed a feature, since it gives enterprising readers of this hack something productive to do right away. Read the last section of this hack for more details on my laziness-slash-goodwill.

Of course, if the ball goes off of the left or right side of the screen without colliding with a paddle, a point has been scored. For now, all SDL Pong does in response to such a joyous occasion is reset the sprites (this time asking `resetSprites()` to erase the sprites from their current location, by virtue of setting the second parameter to a true value), randomly generate a new slope for the ball, then return.

The last thing that `moveBall()` must do, if a point is not scored, is to draw the new location of the ball sprite.

Run `gcc` again to recompile it:

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

and then run the game (see Figure 1):

``./sdl-pong``

Figure 1. Pong, with moving ball

### One Last Score, and then I'm Out

This section addresses the vexing lack of scoring in SDL Pong. So let's open a whole new can of worms: `SDL_TTF`, which is an extra SDL library that deals with TrueType fonts--that is what the "TTF" means. To open the can, include the string.h and SDL_ttf.h headers at the top of sdl-pong.c:

``````// Standard library headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <SDL.h>
#include <SDL_ttf.h>``````

Add some new macros, as well:

``````#define GAME_POINTS 10 // number of points to win the game

#define MSG_FONT "/usr/share/fonts/TTF/Vera.ttf" // font for messages
#define MSG_SIZE 18                              // font size
#define MSG_TIME 1500    // display duration of messages``````

`GAME_POINTS` is self-explanatory; the other three less so. Basically, we are going to use the TrueType font defined by the `MSG_FONT` macro to display some messages in `MSG_SIZE`-point type, and will display the messages for `MSG_TIME` milliseconds.

Note: depending on the whims of your Linux distribution or Unix flavor's package system, your TrueType fonts may not reside in the /usr/share/fonts/TTF directory, and you may not have the Vera.ttf font on your system. That is OK; there is nothing magical about /usr/share/fonts/TTF, and any TrueType font that you have on your system will work just fine. To find your TrueType fonts, either grep your X11 configuration file (most likely /etc/X11/XF86Config or /etc/X11/xorg.conf) for "FontPath", or run: `find / -name \*.ttf`.

Now, you will need a new structure definition:

``````// Structure definitions
typedef struct {

int p1;
int p2;

int game_points;

} Score;``````

And while you are at it, add two new members to the `GameData` structure:

``````  Slope    slope; // slope of the line representing the ball's path
Score    score; // score of the game

TTF_Font *font; // message font``````

Now, you need to initialize the score to 0-0, and the number of points for a win to `GAME_POINTS`:

``````  // Initialise game data
game.running           = 1;
game.ball_speed        = BALL_SPEED;
game.slope_max_dy      = SLOPE_MAX_DY;
game.p1_speed          = P1_SPEED;
game.p2_speed          = P2_SPEED;
game.num_rects         = 0;
game.score.p1          = 0;
game.score.p2          = 0;
game.score.game_points = GAME_POINTS;``````

Right after initializing SDL in your code, add:

``````  // Initialise TTF engine and load a font
TTF_Init();
if ((game.font = TTF_OpenFont( MSG_FONT, MSG_SIZE )) == NULL) {

fprintf( stderr, "Could not open font: %s\n", MSG_FONT );
return cleanUp( 2 );

} // if (could not load font)``````

This performs whatever initialization `SDL_TTF` requires, and then attempts to load the `MSG_FONT` font and scale it to the proper size. Calling `TTF_Init()` introduces a slight new wrinkle: you must tear down `SDL_TTF` before exiting the program. No problem; you can just add a line to the `cleanUp()` function:

``````int cleanUp( int err ) {

TTF_Quit();
SDL_Quit();

return err;

} // cleanUp()``````

Now, all of the remaining action is set in the `moveBall()` function; specifically inside that `else`/`if` block that used to just reset the sprites and regenerate the slope. Let's add scorekeeping code to this block:

``````  // If the ball hits the left or right wall, score a point for the
// appropriate player and return the ball to the centre
else if (game->ball.x < 0 || game->ball.x > (SCREEN_WIDTH - game->ball.w)) {

SDL_Color white = { 0xff, 0xff, 0xff, 0 };

SDL_Rect rect_msg      = { SCREEN_WIDTH / 2 - 90, 100, 200, 50 };
SDL_Rect rect_score_p1 = { 100,                   200, 150, 50 };
SDL_Rect rect_score_p2 = { SCREEN_WIDTH - 200,    200, 150, 50 };

SDL_Rect rects[3];

char str_msg[32], str_score_p1[16], str_score_p2[16];
SDL_Surface *text_msg, *text_score_p1, *text_score_p2;

if (game->ball.x < 0)
game->score.p2++;
else if (game->ball.x > (SCREEN_WIDTH - game->ball.w))
game->score.p1++;

// Write scoring messages
snprintf( str_msg, 32, "Player %d scores!",
((game->ball.x < 0) ? 2 : 1) );

snprintf( str_score_p1, 16, "Player 1: %d", game->score.p1 );
snprintf( str_score_p2, 16, "Player 2: %d", game->score.p2 );

text_msg      = TTF_RenderText_Solid( game->font, str_msg,
white );
text_score_p1 = TTF_RenderText_Solid( game->font, str_score_p1,
white );
text_score_p2 = TTF_RenderText_Solid( game->font, str_score_p2,
white );

// Display scoring messages
rects[0] = rect_msg;
rects[1] = rect_score_p1;
rects[2] = rect_score_p2;

SDL_BlitSurface( text_msg,      NULL, game->screen,
&rect_msg      );
SDL_BlitSurface( text_score_p1, NULL, game->screen,
&rect_score_p1 );
SDL_BlitSurface( text_score_p2, NULL, game->screen,
&rect_score_p2 );

SDL_UpdateRects( game->screen, 3, rects );

// Display the score for awhile
SDL_Delay( MSG_TIME );

// Erase scoring messages
SDL_FillRect( game->screen, &rect_msg,      game->black );
SDL_FillRect( game->screen, &rect_score_p1, game->black );
SDL_FillRect( game->screen, &rect_score_p2, game->black );

SDL_UpdateRects( game->screen, 3, rects );

// Has someone just won the game?
if (game->score.p1 == game->score.game_points ||
game->score.p2 == game->score.game_points) {

// Display the final score
snprintf( str_msg, 32, "Player %d wins!",
((game->ball.x < 0) ? 2 : 1) );

snprintf( str_score_p1, 16, "Player 1: %d", game->score.p1 );
snprintf( str_score_p2, 16, "Player 2: %d", game->score.p2 );

text_msg      = TTF_RenderText_Solid( game->font, str_msg,
white );
text_score_p1 = TTF_RenderText_Solid( game->font, str_score_p1,
white );
text_score_p2 = TTF_RenderText_Solid( game->font, str_score_p2,
white );

rects[0] = rect_msg;
rects[1] = rect_score_p1;
rects[2] = rect_score_p2;

SDL_BlitSurface( text_msg,      NULL, game->screen,
&rect_msg      );
SDL_BlitSurface( text_score_p1, NULL, game->screen,
&rect_score_p1 );
SDL_BlitSurface( text_score_p2, NULL, game->screen,
&rect_score_p2 );

SDL_UpdateRects( game->screen, 3, rects );

// Pause for awhile
SDL_Delay( MSG_TIME * 2 );

// End the game
game->running = 0;
return;

} // if (game over!)``````

The goal of all of this code is simply to display three messages at different locations on the screen. When a player scores, the game displays "Player X scores!" in the top center of the screen, and then each player's new score on his side of the screen. This is accomplished with the help of a slew of local variables: three `SDL_Rect` structures, one for each message (the in-line initialization may be new to some of you; the first field of the `SDL_Rect` structure is the x coordinate, followed by the y coordinate, followed by the width, followed by the height); a local array of `SDL_Rect` structures to feed to our old friend `SDL_UpdateRects()`; a string for each message; and finally, an `SDL_Surface` pointer for each.

The first thing to do is to add a point to the score of the player who just propelled the ball off of the other player's side of the screen. Then, write the three message strings using the standard C function `snprintf()`, provided by the string.h header file (see the `PRINTF(3)` manpage for details on how to use `snprintf()` if it is new to you). Now, call `SDL_TTF`'s `TTF_RenderText_Solid()` function to turn these strings into graphics, draw those graphics onto the main screen using `SDL_BlitSurface()`, then use that old standby, `SDL_UpdateRects()`, to make sure the changes to the main screen are displayed to the user.

After all of this excitement, wait for `MSG_TIME` milliseconds to make sure the players have time to read the new score, and then erase the messages by filling their rectangles with the background color and calling `SDL_UpdateRects()` again.

Now, it is possible that the point that was just score was the game-winner. If so, we go through the string-printing, text-rendering, surface-blitting, rects-updating dance again, but this time, instead of saying "Player X scores!" say "Player X wins!" After this, wait twice the normal `MSG_TIME` to allow for the elaborate cursing that will surely ensue, and then set the running member of the `GameData` structure to a false value (C's only false value being 0, of course), and finally return. This, if you remember the organization of the main loop, will cause control to break out of the loop, and fall through to the end of the `main()` function, which cleans up and exits.

One more time, run `gcc` again to recompile your sdl-pong.c file, this time adding `-lSDL_ttf` to the command-line arguments so that the `libSDL_ttf.so` shared library gets linked in:

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

and then run the game:

``./sdl-pong``

Now you should have SDL Pong in all its glory, as shown in Figure 2.

Figure 2. Pong, with a scoring system

### Hacking the Hack

Though it pains me to admit it, SDL Pong is not a perfect game. There is plenty of room for improvement, and I hope that some readers of this hack who are interested in playing around with SDL will pick up where I've left off.

If you're interested, here is a list, in no particular order, of suggested improvements. Use them as a jumping-off point, or not at all.

• Allow the user to set the various bits of configuration data that exist in the `GameData` structure by passing arguments on the command line.

• The collision detection algorithm I've used for figuring out when the ball has hit the paddle is not so great, as evidenced by the damage done to the paddle by the ball. This is simply because the ball moves between one and five pixels at a time in the x direction, and the paddles are five pixels wide, and happen to present an edge to the ball that starts at an x coordinate that is not usually divisible by the change-in-x component of the slope. Add math until it works properly.

• This is related to collision detection: fix the bug that allows the ball to bounce off of the inside of a paddle, if it hits the top or bottom edge of the paddle just right.

• Monochrome is so '70s. Colorize Pong.

• Using flood-filled rectangles for graphics is also so '70s. Investigate `SDL_image` and make Pong use PNG, JPG, or GIF graphics. Better yet, let the user select the paddle and ball graphics, and scale them to the appropriate size dynamically.

• My physics model is simplistic and boring. Add "english" to the ball depending on which direction, and maybe even how fast, the paddle is moving when it hits the ball.

• Any game with power-ups and specials is better. Add effects like exploding balls, Hyper Power Zero Shots, time stopping, and so on. Better yet, make these power-ups obtainable between rounds by trading in "style points" or some such scoring invention.

• Sound: `SDL_mixer` will let you provide an Ogg Vorbis soundtrack to the game, so you can add popping sound effects when the paddle strikes the ball. And maybe some crowd noise when a point is scored.

• Add network play using `SDL_net`. Make sure to come up with some way to find games. For a challenge, encapsulate your network protocol in HTTP (it might take a week to play a point, but it would be cool).

### Resources

If you're looking for more information to help you become a cross-platform, game-programming wizard, be sure to check out some of the following resources:

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.