Let’s use what we know now to enhance the animation we constructed in our last examples.
In the animation we have constructed so far, we placed a single image on the screen at a location we specified in the code. Suppose we want to place more than one image on the screen and let the user control where these images will be located, and in what direction they will move initially.
One common method for doing this is to use a text file to control the startup og the program. We will not get too fancy with this file for now. We will set up just enough information to get our animation going.
We will use a file to define the number of balls we want to see, then set up a series of numbers for each ball specifying where the ball will be placed (X,Y) and the initial velocity of that ball(Vx,Vy). We used these data items in our original program.
Here is a sample file we might use:
3
100
100
2
3
300
300
4
1
500
500
1
3
The reason we constructed the file this way has to do with now we will need to access the file using only DarkGDK file routines.
We use the following routines to access this text file:
| Name | Purpose | ||
| dbFileExist(char * filename) | Does the named file exist (boolean) | ||
| dbOpenToRead(int fID | char * filename) | Open for reading | use the ID |
| dbReadString(int fID) | Read string from file ID | ||
| dbCloseFile(fID) | Close the file |
Note
In the directory where you installed DarkGDK you will find a help file containing the detailed list of functions supported by the library. Use this to find other useful functions for your code!
The DarkGDK string read routine loads a single line of text into memory and returns the address of the memory location. We need to use the standard C++ string copy function to move this string into a buffer in our program, then convert it into a usable form.
Here is the code we need to read this file.
const int INPUT_FILE = 1;
...
void getConfig() {
char buffer[128];
dbOpenToRead(INPUT_FILE,file_name);
strcpy(buffer,dbReadString(INPUT_FILE));
NUM_BALLS = atoi(buffer);
for(int nb=0;nb<NUM_BALLS;nb++){
strcpy(buffer,dbReadString(INPUT_FILE));
position[nb][0] = atoi(buffer);
strcpy(buffer,dbReadString(INPUT_FILE));
position[nb][1] = atoi(buffer);
strcpy(buffer,dbReadString(INPUT_FILE));
speed[nb][0] = atoi(buffer);
strcpy(buffer,dbReadString(INPUT_FILE));
speed[nb][1] = atoi(buffer);
}
}
Changing the code we used to display a single image so we show multiple images is simple. We will use two arrays to store ball position and speed and loop over the number of balls to display the set:
const int MAX_BALLS = 10;
int speed[MAX_BALLS][2];
int position[MAX_BALLS][2];
Here is the loop we use to display the balls:
while (LoopGDK()) {
dbCLS(background);
for(int nb=0;nb<NUM_BALLS;nb++) {
dbSprite(nb+1,position[nb][0], position[nb][1], 1);
TableMover(NUM_BALLS, position, speed, EARTH_RADIUS);
checkCollisions(NUM_BALLS, position, speed, EARTH_RADIUS);
}
dbSync();
}
In this example, we modify the TableMover routine used to update ball positions and handle the collisions with the walls. We pass in the arrays holding the position and speed data and let the routine update all ball position and speed data using the same rules we used before. Since the ball position and speed arrays are passed by address, the changes in the routine will be felt throughout the program.
The new bit of code we want to include in this program handles the collisions between the balls so they appear to bounce off of each other. The physics of such collisions is an extension of the method we used for the balls bouncing off of the walls. However, the math gets a bit messier since balls can collide in almost any configuration in the middle of the screen. So, how do we handle this?
The math we want to research is based on the principle of Conservation of Momentum. There are some odd properties of this principle that we want to model. For instance, if a ball at rest is hit by a ball moving straight at it, the first ball will take on the velocity of the second ball and the second ball will stop! Yikes!
Here is an animation showing what we need to do:

Note
In this example, one ball is moving, and one stationary. In the general case, both balls are moving. (I could not generate a simple animation for that case).
Finding the code you need is a bit difficult. It might be easier to derive the equations yourself. Here is the basic logic:
We are given two balls with equal radii and mass. They are moving at a speed defined by the two vector components Vx and Vy. They have a position on the coordinate system defined by X and Y. We will use a trailing number to refer to each ball. So, here are our given parameters:
| Vx1 | velocity of nball 1 in the X direction (right) |
| Vy1 | velocity of ball 1 in the Y direction (down) |
| X1 | X position of ball 1 |
| Y1 | Y position of ball 1 |
| Vx2 | velocity of nball 2 in the X direction (right) |
| Vy2 | velocity of ball 2 in the Y direction (down) |
| X2 | X position of ball 2 |
| Y2 | Y position of ball 2 |
If the distance between the centers of the ball is less than or equal to twice the radius of a ball, we must have collided. Here is the basic layout of the event:

At that point, we need to determine what happens when the collision occurs. Here is some code we could use to determine if two balls have collided:
bool collided(float X1, float Y1, float X2, float Y2, float radius) {
float dx = X2 - X1;
float dy = Y2 - Y1;
float dist = sqrt(dx*dx + dy*dy);
return dist <= (2.0*radius);
}
In order to deal with a set of balls, we need to check every ball against every other ball to see if we have any collisions. However, this will result in checking more cases than needed if we are not careful. If we check ball 1 against ball 2, we do not need to check ball 2 against ball 1 again, we already did that!. And a ball certainly cannot bounce against itself. So, if we use a simple integer to refer to a ball, we can set up a system to check all cases using nested loops. For N balls, we use this:
for(ball1 = 0;ball1<N;ball1++) // check each ball in turn
for(ball2=ball1+1;ball2<N;ball2++) { // check all other balls
// check for collision between ball1 and ball 2
if collided()) {
handleCollisison()
}
}
To make the code easier to construct, we will set up two arrays to hold speed and position data. These arrays will be named:
We also store the radius:
We are using floats to allow for precise calculations of speed. We will convert these values back to integer for use in the graphics, losing some accuracy, but the simulation should be adequate!
Now, the collided function can be called with two integers referring to the two balls to check:
bool collided(int ball1, int ball2, int rad) {
float X1 = pos[ball1][0];
float Y1 = pos[ball1][1];
float X2 = pos[ball2][0];
float Y2 = pos[ball2][1];
float dx = X2 - X1;
float dy = Y2 - Y1;
float dist = sqrt(dx*dx + dy*dy);
return dist <= (2.0*radius);
}
If we have the velocity of the ball expressed in the X component and the Y component, the total velocity is:
The angle between the velocity vectors and the X axis is:
Note
To avoid division problems in determining the angle, we should use the atan2 function!
The angle between a line drawn between the two ball centers is defined as:
We will be transforming the velocity vector into components along the centerline and perpendicular to it. Here is the projection we will use:

The angle between the velocity vectors and the centerline are calculated as:
The component of the velocity vector along the centerline is given by:
The collision satisfies law of conservation of momentum. In breaking the velocity into components along the centerline and perpendicular to is, we can satisfy this conservation law by leaving the velocities perpendicular to the centerline alone, and dealing with just those along the centerline (which is a one dimensional collision). The basic law can be satisfied using this:
This is true only if the mass of each ball is the same.
Once we know the new total velocity, we can figure out the new direction, and from that the nex Vx and Vy components:
The final angle between the new velocity vectors can be calculated by the following code:
Note
The derivation from this point is incomplete while I build a few more graphics. In the meantime, the final code will be chown:
Here is the collision code. This code can deal with two balls of unequal mass, but needs to be modified to allow the game to deal with this situation. Perhaps we could calculate the mass from the ball radius if ball sizes could vary. That would be an enhancement of the code for a more general game (when planets collide, for example!)
const float mass1 = 1.0; // for now
const float mass2 = 1.0;
void handleCollision(ball, ball2) {
dx = X1-X2;
dy = ball._y-Y2;
angleCL = atan2(dy, dx);
V1 = sqrt(Vx1*Vx1+Vy1*Vy1);
V2 = sqrt(Vx2*Vx2+Vy2*Vy2);
angle1 = atan2(Vy1, Vx1);
angle2 = atan2(Vy2, Vx2);
new_xspeed_1 = V1*cos(angle1-angleCL);
new_yspeed_1 = V1*sin(angle1-angleCL);
new_xspeed_2 = V2*cos(angle2-angleCL);
new_yspeed_2 = V2*sin(angle2-angleCL);
final_xspeed_1 = ((mass1-mass2)*new_xspeed_1+
(mass1+mass2)*new_xspeed_2)/(mass1+mass2);
final_xspeed_2 = ((ball.mass+ball.mass)*new_xspeed_1+
(mass2-mass1)*new_xspeed_2)/(mass1+mass2);
final_yspeed_1 = new_yspeed_1;
final_yspeed_2 = new_yspeed_2;
Vx1 = cos(angleCL)*final_xspeed_1+cos(angleCL+PI/2)*final_yspeed_1;
Vy1 = sin(angleCL)*final_xspeed_1+sin(angleCL+PI/2)*final_yspeed_1;
Vx2 = cos(angleCL)*final_xspeed_2+cos(angleCL+PI/2)*final_yspeed_2;
Vy2 = sin(angleCL)*final_xspeed_2+sin(angleCL+PI/2)*final_yspeed_2;
}