Retro Gaming Hacks, Part 3: Add a Ball and Score to Pong
Pages: 1, 2, 3, 4, 5, 6
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.