Graphics Programming using Allegro

Bouncing Ball with Image Background

Published:  September 17, 2008
by Richard G. Baldwin

File:  Allegro00160


Preface

General

This lesson is part of a series (see Resources) designed to teach you how to use Allegro to do graphics programming in C++.  My purpose in writing the series is to provide lecture and lab material for a course titled Game Development Using C++ that I teach at Austin Community College in Austin, Texas.  However, if you have stumbled upon this series and you find it useful, you are welcome to study it.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

Listings

Supplemental material

I recommend that you also study the other lessons in my extensive collection of online programming tutorials.  You will find a consolidated index at www.DickBaldwin.com.

Preview

This is an update of an earlier Bouncing-Ball Animation program (see Lesson 125 in Resources).  This update illustrates two important aspects of animation:

The program displays a red ball bouncing around inside of a box in front of an image of a starfish as shown in Figure 1.

Figure 1. Screen output from program BouncingBall02.

The ball is allowed to move in only four directions:

When the ball hits a wall, it doesn't always bounce in the direction that would normally be expected.  Instead it selects one of the four possible directions at random and attempts to bounce in that direction.  The ball continues to bounce until the user presses the Esc key, at which time the program terminates.

Discussion and sample code

As usual, I will explain the program code in fragments.  A complete listing of the program is provided in Listing 10 near the end of the lesson.

Beginning of the program

The program code begins in Listing 1.

Listing 1. Beginning of the program named BouncingBall02.
#include <allegro.h>

//Declare a pointer variable named buffer that can be
// used to point to a BITMAP. This will be the main
// memory buffer containing an image.
BITMAP *buffer = NULL;


BITMAP *smallBuffer = NULL;


//Note that the size of the image was known before the
// program was written, and that size is used below to
// set the size of the box.
int width = 324;//width of box
int height = 330;//height of box
int radius = 9;//radius of ball

int x = 100;//initial position of ball
int y = 200;//initial position of ball

int tempX;//used to save the current location of the ball
int tempY;//used to save the current location of the ball

//Keep track of direction of motion here.
//0= northwest 1 = southwest, 2 = northeast,
//3 = southeast
int dir;

The pointer variable named smallBuffer

Most of the code in Listing 1 should be familiar to you by now and shouldn't require any explanation beyond the embedded comments.  However, the pointer variable named smallBuffer does deserve an explanation because it is new to this program.

Must erase the old image of the ball

In the earlier bouncing-ball program, a ball was drawn moving in front of a black background.  In both programs, whenever it is time for the ball to move, the existing image of the ball must be erased before drawing the ball in its new location.  With a solid-color background, all that is required to erase the ball is to draw another ball on top of the existing ball in the background color.  However, a little more work is required to erase the ball when the background is an image instead of a solid color.

Save a small portion of the background image

The pointer variable named smallBuffer will be used to point to a BITMAP buffer in memory.  This memory buffer will be used to save a small portion of the background image centered on the new location of the ball before the ball is drawn in that location on the image.  The small image will then be used to restore the area of the image occupied by the ball when it is time for the ball to move to a new location.  In other words, the small square image will be drawn in its original location, effectively erasing the current image of the ball and restoring the image behind the ball at the same time.

Beginning of the main function

This program contains two functions:

I will begin my explanation with the main function, which begins in Listing 2.

Listing 2. Beginning of the main function.
int main(){

  allegro_init();
  install_keyboard();
  set_color_depth(32);
  set_gfx_mode(GFX_AUTODETECT_WINDOWED,width,height,0,0);

  //Load an image file from the current directory.
  buffer = load_bitmap("starfish324x330.pcx", NULL);

I have explained code similar to the code in Listing 2 before (see Resources), so I won't repeat those explanations here.

 
The bitmap isn't really empty
As you learned in an earlier lesson, the new bitmap contains garbage, but that won't be a problem for us in this program.  We will fill it with new data and overwrite the existing garbage.

Prepare the bitmap for the small image

Listing 3 calls the create_bitmap function to create an empty bitmap and store its address in the pointer variable named smallBuffer.

Listing 3. Prepare the bitmap for the small image.
  smallBuffer = create_bitmap(radius*2 + 2,radius*2 + 2);

The width and height of the small bitmap are equal to the diameter of the ball plus two pixels.

Save a square portion of the background image

Listing 4 calls the blit function to copy and save a square area of the background image surrounding the location at which the ball will be drawn when it first appears on the screen.  (I have discussed the use of the blit function in earlier lessons, so an explanation should not be needed here.)

Listing 4. Save a square portion of the background image.
  blit(buffer,smallBuffer,
       x-radius-1,y-radius-1,
       0,0,
       radius*2+2,radius*2+2);

Note that I allowed each side of the square being copied to be two pixels larger than the diameter of the ball to allow for the possibility that the ball isn't perfectly centered in the square.

The animation loop

Listing 5 calls the srand function and the rand function to establish a random initial direction for the ball.  I explained code very similar to this code in the earlier bouncing-ball program (see Resources).

Listing 5. The animation loop.
  //Seed the random number generator and set the initial
  // direction based on a random number.
  srand (time(NULL));
  dir = rand() % 4;
  
  //Loop until the user presses the Esc key.
  while( !key[KEY_ESC]){
    moveBall();
  }//end while loop

  return 0;
}//end main
END_OF_MAIN();

Then the code in Listing 5 enters the animation loop, where it calls the moveBall function once during each iteration.  The loop will continue to iterate, and the ball shown in Figure 1 will continue to move in front of the starfish image until the user presses the Esc key, at which time the program will terminate.

Beginning of the moveBall function

The moveBall function begins in Listing 6.

Listing 6. Beginning of the moveBall function.
void moveBall(){
  //Save current location of ball.
  tempX = x;
  tempY = y;

  //The code in the switch statement is identical to the
  // code in the earlier version of the program.
  switch(dir){
    case 0:
      //Direction is northwest.
      if((x <= radius) || (y <= radius)){
        //Ball has collided with either the left wall or
        // the top wall.
        //Get a new direction. Note that if the new
        // direction is the same as the old one, control
        // will come back to here to get still another
        // direction the next time the function is called.
        dir = rand() % 4;
      }else{
        //No collision, set new location for the ball
        --x;
        --y;
      }//end else
    break;
    case 1:
      //Direction is southwest.
      if(((x <= radius) || (y >= (height - radius)))){
        //get a new direction
        dir = rand() % 4;
      }else{
        //set new location for the ball
        --x;
        ++y;
      }//end else
    break;
    case 2:
      //Direction is northeast.
      if(((x >= (width - radius)) || (y <= radius))){
        //get a new direction
        dir = rand() % 4;
      }else{
        //set new location for the ball
        ++x;
        --y;
      }//end else
    break;
    case 3:
      //Direction is southeast
      if((((x >= (width - radius)) ||
                              (y >= (height - radius))))){
        //get a new direction
        dir = rand() % 4;
      }else{
        //set new location for the ball
        ++x;
        ++y;
      }//end else

  }//end switch

The code in Listing 6:

This code is very similar to code that I explained in the earlier bouncing-ball program (see Resources), so I won't repeat that explanation here.

Erase the current image of the ball

This is where things really depart from the earlier bouncing-ball program.  Listing 7 erases the current image of the ball by calling the blit function to draw the previously-saved portion of the background image over the ball.  This restores the background image to its state before the ball was drawn on top of the image during the previous iteration of the animation loop.

Listing 7. Erase the current image of the ball.
  blit(smallBuffer,buffer,
       0,0,
       tempX-radius-1,tempY-radius-1,
       radius*2+2,radius*2+2);

Note that the position coordinates that were saved at the beginning of Listing 6 are used to establish the location at which the previously-saved portion of the background image is drawn.

Save a new portion of the background image

Listing 8 saves a square area of the background image at the new location of the ball (computed in Listing 6) before the ball is drawn in its new location.

Listing 8. Save a new portion of the background image.
  blit(buffer,smallBuffer,
       x-radius-1,y-radius-1,
       0,0,
       radius*2+2,radius*2+2);

As was the case when a small portion of the background image was saved in Listing 4, each side of the square is two pixels larger than the diameter of the ball to allow for the possibility that the ball isn't perfectly centered in the square.

Completion of the moveBall function

Listing 9 completes the moveBall function by:

Listing 9. Completion of the moveBall function.
  //Now draw the ball on the background at the new
  // location.
  circlefill (buffer,x,y,radius,makecol(255,0,0));

  //Call the blit function to copy the off-screen buffer
  // contents to the screen.
  blit(buffer,screen,0,0,0,0,width,height);

  rest(5);//Delay for five milliseconds
}// end moveBall function.

Then Listing 9 returns control to the code in the animation loop in Listing 5 where the process continues to repeat until the user presses the Esc key.

Summary

In this lesson, you learned how to implement two important aspects of animation:

Complete program listing

A complete listing of the program is shown in Listing 10.

Listing 10. Source code for program named BouncingBall02.
/*Project BouncingBall02
This is an update of the earlier program named
BouncingBallInBox.

This update illustrates two important aspects of smooth
animation:

1. Drawing the animated ball on an off-screen buffer and
calling the blit function to copy the contents of the
buffer to the screen once during each animation cycle.
2. Moving an object in front of a background that is
an image instead of a solid color as was the case in the
earlier version of the program.

Displays a ball bouncing around inside of a box. The
ball is allowed to move in only four directions:
northeast, southeast, southwest, or northwest. When the
ball hits a wall of the box, it doesn't bounce in the
direction that would normally be expected.  Instead it
selects one of the four possible directions and bounces
in that direction.

The ball continues to bounce until the user presses the
Esc key, at which point the program terminates.
*/

#include <allegro.h>

//Declare a pointer variable named buffer that can be
// used to point to a BITMAP. This will be the main
// memory buffer containing an image.
BITMAP *buffer = NULL;

//Declare another pointer variable named smallBuffer that
// can also be used to point to a BITMAP. This memory
// buffer will be used to save a small square area of the
// large image centered on the ball. The small image will
// be used to restore the area of the image occupied by
// the ball when it is time for the ball to move to a
// new location.
BITMAP *smallBuffer = NULL;

//Note that the size of the image was known before the
// program was written, and that size is used below to
// set the size of the box.
int width = 324;//width of box
int height = 330;//height of box
int radius = 9;//radius of ball

int x = 100;//initial position of ball
int y = 200;//initial position of ball

int tempX;//used to save the current location of the ball
int tempY;//used to save the current location of the ball

//Keep track of direction of motion here.
//0= northwest 1 = southwest, 2 = northeast,
//3 = southeast
int dir;
//------------------------------------------------------//

void moveBall(){
  //Save current location of ball.
  tempX = x;
  tempY = y;

  //The code in the switch statement is identical to the
  // code in the earlier version of the program.
  switch(dir){
    case 0:
      //Direction is northwest.
      if((x <= radius) || (y <= radius)){
        //Ball has collided with either the left wall or
        // the top wall.
        //Get a new direction. Note that if the new
        // direction is the same as the old one, control
        // will come back to here to get still another
        // direction the next time the function is called.
        dir = rand() % 4;
      }else{
        //No collision, set new location for the ball
        --x;
        --y;
      }//end else
    break;
    case 1:
      //Direction is southwest.
      if(((x <= radius) || (y >= (height - radius)))){
        //get a new direction
        dir = rand() % 4;
      }else{
        //set new location for the ball
        --x;
        ++y;
      }//end else
    break;
    case 2:
      //Direction is northeast.
      if(((x >= (width - radius)) || (y <= radius))){
        //get a new direction
        dir = rand() % 4;
      }else{
        //set new location for the ball
        ++x;
        --y;
      }//end else
    break;
    case 3:
      //Direction is southeast
      if((((x >= (width - radius)) ||
                              (y >= (height - radius))))){
        //get a new direction
        dir = rand() % 4;
      }else{
        //set new location for the ball
        ++x;
        ++y;
      }//end else

  }//end switch

  //Erase the current image of the ball by drawing the
  // saved background over the ball.
  blit(smallBuffer,buffer,
       0,0,
       tempX-radius-1,tempY-radius-1,
       radius*2+2,radius*2+2);

  //Save a square area of the background at the new
  // location of the ball before the ball is drawn there.
  // Each side of the square is two pixels larger than the
  // diameter of the ball to allow for the possibility
  // that the ball isn't perfectly centered in the square.
  blit(buffer,smallBuffer,
       x-radius-1,y-radius-1,
       0,0,
       radius*2+2,radius*2+2);

  //Now draw the ball on the background at the new
  // location.
  circlefill (buffer,x,y,radius,makecol(255,0,0));

  //Call the blit function to copy the off-screen buffer
  // contents to the screen.
  blit(buffer,screen,0,0,0,0,width,height);

  rest(5);//Delay for five milliseconds
}// end moveBall function.
//------------------------------------------------------//

int main(){

  allegro_init();
  install_keyboard();
  set_color_depth(32);
  set_gfx_mode(GFX_AUTODETECT_WINDOWED,width,height,0,0);

  //Load an image file from the current directory.
  buffer = load_bitmap("starfish324x330.pcx", NULL);
  
  //Create an empty bitmap and store its address in
  // smallBuffer. Make the width and height equal to the
  // diameter of the ball plus two pixels.
  smallBuffer = create_bitmap(radius*2 + 2,radius*2 + 2);
  
  //Save a square area of the background at the current
  // location of the ball. Each side of the square is two
  // pixels larger than the diameter of the ball to allow
  // for the possibility that the ball isn't perfectly
  // centered in the square.
  blit(buffer,smallBuffer,
       x-radius-1,y-radius-1,
       0,0,
       radius*2+2,radius*2+2);

  //Seed the random number generator and set the initial
  // direction based on a random number.
  srand (time(NULL));
  dir = rand() % 4;
  //Loop until the user presses the Esc key.
  while( !key[KEY_ESC]){
    moveBall();
  }//end while loop

  return 0;
}//end main
END_OF_MAIN();

 

Resources


Copyright

Copyright 2008, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-