oreilly.comSafari Books Online.Conferences.


Retro Gaming Hacks, Part 2: Add Paddles to Pong
Pages: 1, 2, 3, 4, 5, 6

The next step is to compute the new Y coordinate of the paddle, and then decide how far it has moved, as shown in this excerpt:

  // Compute the new y coordinate of the rectangle and the pixels moved
  new_y = (dir == DIR_UP ? (rect->y - speed) : (rect->y + speed));
  moved = (dir == DIR_UP ? (rect->y - new_y) : (new_y - rect->y));

The equation for determining the new Y coordinate is quite simple. If the paddle is moving up, subtract the speed from the current Y coordinate; otherwise, the paddle is moving down, so add the speed to the current Y coordinate (remember that Y values grow downwards in a left-handed coordinate system). To determine how many pixels were moved, we just subtract the new Y coordinate from the old one. This is so that you can detect whether full movement would take the paddle off of the top or bottom of the screen (which you decidedly do not want). Here's how movePaddle() does it:

  // If the move would take us off the top or bottom of the screen,
  // we may have to move less than speed
  if (dir == DIR_UP && new_y < 0) {

    new_y = 0;
    moved = rect->y - new_y;
  } // if (moving up less than speed)
  else if (dir == DIR_DOWN && new_y > SCREEN_HEIGHT - rect->h) {

    new_y = SCREEN_HEIGHT - rect->h;
    moved = new_y - rect->y;
  } // else if (moving down less than speed)

If you detect that the paddle has moved too far, simply reset the new_y variable to the minimum value, if moving up, or the maximum one, if moving down. Remember, the X and Y coordinates of the paddle represent its upper left-hand corner, so when detecting movement off the bottom of the screen, we need to subtract the height of the paddle from SCREEN_HEIGHT. After resetting new_y, you must also recalculate how far the paddle has moved. It could be that the paddle has not moved at all, if it was already at the top of the screen and the player tried to move up, or if it was at the bottom and the player tried to move down. If this is the case, you can just return from the movePaddle() function:

  // If we have not moved, just return
  if (moved == 0)

Now you need to handle the movement graphically. The first thing to do is to erase the paddle, but instead of simply filling the entire rectangle corresponding to the current location of the paddle with black (the background color), fill only the bit that changed. This is to eliminate flicker, the dreaded condition where the user can actually perceive the redraw operation--it looks like an old movie that was shot at fewer frames per second than modern ones. (Another way to avoid flickering is to use double-buffering, which SDL makes quite easy, but you must use a hardware surface for your main window, which we have avoided in this hack for the sake of code clarity and also to ensure that SDL Pong runs well on a wide variety of computing machinery.)

The region of the screen that has changed can be represented by a rectangle (and this is why we declared the tmp variable at the top of the function) with the same X coordinate as the paddle (since the paddle cannot move horizontally, only vertically) and the same Y coordinate as the paddle, if moving down (since we need to erase the top few lines of the paddle); or the paddle's Y coordinate, plus the height of the paddle, minus the number of pixels moved. The width of the rectangle is the same as the width of the paddle, and the height is the number of pixels moved, whether moving up or down:

  // Erase the top or bottom line(s) of the paddle
  tmp.x = rect->x;
  tmp.y = (dir == DIR_UP ? (rect->y + rect->h - moved) : rect->y );
  tmp.w = rect->w;
  tmp.h = moved;

If this numbers game does not make sense, try sketching out the rectangles on a piece of scratch paper (or graph paper, if you have it).

To actually erase the region, we call SDL_FillRect() with the following parameters: the screen pointer, a pointer to the tmp rectangle, and the background color (black). Then, we add the rectangle to the rects member of the GameData structure--the array of rectangles that are automatically updated at the beginning of the main loop, thanks to the coding we did in the sprites section of this hack.

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

You now need to update the SDL_Rect structure corresponding to the paddle with the new Y coordinate so that movePaddle() knows where the paddle is next time it is called. We are nearing the end now; the only thing left is filling the new top or bottom region of the paddle. To do this, simply set the Y coordinate of the temporary rectangle, tmp, to the new Y coordinate of the paddle, if moving up. Or, if moving down, the new Y coordinate plus the height of the paddle, minus the number of pixels moved. You can then proceed to fill the temporary rectangle with the foreground color (white) in the normal fashion:

  // Draw the new bottom or top line(s) of the paddle
  tmp.y = (dir == DIR_UP ? rect->y : (rect->y + rect->h - moved) );
  SDL_FillRect( game->screen, &tmp, game->white );
  game->rects[game->num_rects++] = tmp;

} // movePaddle()

Run gcc again to re-compile it:

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

Now run the game:


You'll be able to move the paddles around (Figure 2), but you still need to add a moving ball--check back the first week of the new year, when we'll cover just how to do that.

Figure 2
Figure 2. Pong, with moving paddles

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

Return to the Linux DevCenter.

Linux Online Certification

Linux/Unix System Administration Certificate Series
Linux/Unix System Administration Certificate Series — This course series targets both beginning and intermediate Linux/Unix users who want to acquire advanced system administration skills, and to back those skills up with a Certificate from the University of Illinois Office of Continuing Education.

Enroll today!

Linux Resources
  • Linux Online
  • The Linux FAQ
  • Linux Kernel Archives
  • Kernel Traffic

  • Sponsored by: