Bouncing off things

Once we start moving the poolball on the screen we need to watch out for collisions!

We have already explored how to handle the collisions with the edges of the table. However, when we have more than one ball moving on the table, we have a problem.

Who watches for ball on ball collisions?

Let’s consider two ways we might shape a graphics program that draws poolballs on a screen:

while (!keypressed()) {
    myscreen.erase(ball);
        ball.move();
        myscreen.display(table);
        myscreen.display(ball);
    }

Why did I write it this way? Why not just call one simple routine to do all this work in one step? Like this:

while (!keypressed()) {
   myscreen.updateScreen(ball);
}
...
MyScreen::updateScreen(Poolball ball) {
    ...
    ball.move();
    ...
}

What will happen here? Well, we call the updateScreen method and send down a copy of the ball object. Inside the routine we call the ball’s move method which will update the instance variables to make the ball move. But, what variables were updated? Unfortunately, not the original ball’s, but the copies! That is not good. When we exit the updateScreen function, all those changes inside the object are lost, just like they would have been in a simple value parameter.

We can solve this problem by passing a reference to the object, in which case modifications to the object inside the function will be made to the original object. It is far too easy to forget all of this when you first start writing object oriented code using your normal way of thinking.

We have not really answered the question! Is a bit-wise copy of the object adequate. In this problem, the answer is yes, but in many cases, the answer is emphatically NO! We can see a glimmer of why in our Poolball object. Contained inside that object are a number of attributes, each with unique values. If we copy whatever them, will everything still work?

In this simple case, everything should work, but as our objects get more complicated, the answer might not be so clear. As we build more complex objects, we will need to revisit this whole copying issue.

Moving a bunch of balls

We have provided most of the code needed to build our complete poolball demo. We have not explained how to make balls bounce off of each other, though.

Let’s think about this. How did we handle a single ball bouncing off of a wall?

Well, first, we came up with a way to determine if the ball had even hit the wall. We did this by calculating how far the center of the ball was from the wall. If that distance was less than the radius of the ball, we trigger a bounce. Sine our simulation is working by moving a clock forward in distinct ticks, we will not be able to see the exact moment a collision happens, but we will be close enough for our demonstration purposes.

Now, how will we determine if one ball has hit another ball - given that we have a number of them?

We can create an array of Poolball objects as a simple way to manage the collection. As discussed earlier, we will place this array in the Table class, and make an instance of that class (our actual pool table) manage the pool balls.

Our Poolball objects already know how to move, we included that behavior in the Poolball class. However, in our rewrite of the demo program, we did not add the wall collision logic to the Poolball class. Why?

The answer is that we are making the Pooltable object responsible for watching each and every poolball object, and that includes watching to see if they have hit a wall, or another ball. The two events are different, and involve different calculations.

Bouncing off the walls

Here is the logic we need to add to our Pooltable class to make it watch the wall collisions for every ball in our array of balls:

// now loop over all the balls in the simulation
for(int i=0;i<MAXBALLS;i++) {

    // let this ball move
    balls[i].move();

    // see if we hit the wall
    if (balls[i].getX() > (screenx + width - 2 * rad))
        balls[i].setVx(-balls[i].getVx());
    else if (balls[i].getX() < screenx)
        balls[i].setVx(-balls[i].getVx());
    if (balls[i].getY() > (screeny + height - 2*rad))
        balls[i].setVy(-balls[i].getVy());
    else if(balls[i].getY() < screeny)
        balls[i].setVy(-balls[i].getVy());
}

See how this works? We loop over all balls in our collection moving each and then checking to see if any of the four walls have been hit. If so, we adjust the velocity according to rules we discussed earlier. Why do I check the walls this way? It is possible that we hit two walls at the same time, so we allow that to happen. (It is not possible to hit both vertical or horizontal walls at the same time, though).

Bouncing off each other

The logic for managing the ball-to-ball collisions is similar. Except now we need to check every ball against every other ball and see if we need to adjust the velocity. Here is a loop that might do the job:

for(int i=0;i<MAXBALLS;i++) {
    for(int j=0;j<MAXBALLS;j++) {

        // skip a ball hitting itself
        if (i == j) continue;
        checkCollision(i,j);
    }
}

We skip the obvious situation where a ball collides with itself, and check every other possible collision. If you think about it, we could end up checking every actual collision twice - once for each ball involved in the collision. A simple (and totally wrong) method for simulating the collision is to treat the second ball in the collision as a fixed wall that is aligned perpendicular to the line between the centers of the two balls and just bounce the same way we did with the table walls.

Why is this wrong? Well, when two balls moving at different speeds run into each other, we need to determine how the shared momentum of those balls is redistributed back into each ball, and set each ball’s new velocity correctly. This involves more work than I want to go through here, so I will do the simple (and wrong) thing and see what it looks like!

Here is the situation:

../_images/collision.jpg

What we need to do is to break the total velocity of each ball into two parts, one along the line between the two centers, and one perpendicular to that line. We will bounce the ball by reversing the normal part, then recalculating the velocity in the x and y directions so we can update the ball’s velocity. Simple, huh? Yep, except for the math. Which is here:

dx = x2 - x1;
dy = y2 - y1;
dist = sqrt(dx*dx+dy*dy);
if (dist < 2.0*rad) {
    // they did, adjust their velocity
    fi = atan(dy/dx);
    vn1 = vx1*cos(fi) + vy1*sin(fi);
    vt1 = -vx1*sin(fi)+ vy1*cos(fi);
    //bounce - with bad physics!
    vn1 = -vn1;
    vx1 = vn1*cos(fi)-vt1*sin(fi);
    vy1 = vn1*sin(fi)+vt1*cos(fi);
}

Most of these variables need to be floats, and you need to include the <math.h> library to get the functions we need here. The 1 in each variable refers to ball 1, and the 2 to ball 2. The normal and tabgential velocities are vn and vt in the above. Don’t worry about the derivation of all of this, this is not a math (or trig) course!