Bouncing Off Walls

While it’s neat that we can create a vector with magnitude and
direction and then move an object along it infinitely, it’s probably not
something you will need to do all that often. Most of the time, you will
want to see that object react to the world around it by bouncing off
horizontal and vertical walls, for example.

To help you understand how to do this, there is a simple rule in
physics. Although this rule is usually applied to rays of light, it can be
very useful when animating 2D objects—especially when they are bouncing
off horizontal and vertical walls. This rule is known as the
angle of reflection:

The angle of incidence is equal to the angle of reflection.

The angle of incidence is the angle an object
is traveling when it hits the walls, and the angle of
reflection is the angle it travels after it bounces off the
wall.

Figure 5-4
illustrates that when an object hits a wall on a line that forms a
45-degree angle with a perpendicular line drawn to the point of impact, it
will bounce off (reflect) at a similar 45-degree angle.

Figure 5-4. Angle of incidence is equal to the angle of reflection

In the next section, we will create a series of examples using this
rule to animate objects. The first, Example 5-4, will
simply allow a single ball to bounce off the edges of the canvas.

Bouncing a Single Ball

In this first example, we will create a ball traveling on a
vector. We will set the speed
(magnitude) to 5 and the angle (direction) to 35 degrees. The rest of the variables are
identical to those in Example 5-3. We are
still moving on a vector, but now we will test to see whether the ball
hits a “wall” (the edges of the canvas), in which case it will bounce
off, using the rule of the angle of reflection. One big change from the
previous vector example is the location in which we initialize the
values for radians, xunits, and yunits. Instead of setting them up when we
initialize the application in canvasApp(), we save that for a call to a new
function named updateBall():

The updateBall() function is
called every time we set a new angle
for the ball, because we need to recalculate the radians and find new values for xunits and yunits. A new angle is generated when the app starts, as
well as every time the ball bounces off a wall:

Next, we test to see whether the ball has hit a wall before we
draw it to the canvas. If the ball hits the right side (ball.x > the Canvas.width) or the left side
of the canvas (ball.x < 0), we set
the angle to 180 degrees minus the angle of the vector on which the ball
is traveling. This gives us the angle of reflection. Alternatively, if
the ball hits the top (ball.y < 0)
or bottom (ball.y >
theCanvas.height) of the canvas, we calculate the angle of
reflection with 360 degrees minus the angle of the vector on which the
ball is traveling:

Note

The points on the line are not drawn when executed in the web
browser because they slowed down the ball far too much. We left them
in Figure 5-5 to illustrate
the angles of incidence and reflection.

Multiple Balls Bouncing Off Walls

One ball is cool, but what about 100? Is the code 100 times more
complicated? No, not at all. In fact, the code is only slightly more
complicated, but it is also more refined. Most programming tasks that
require only a single object of a type tend to allow you to be a bit
lazy. However, when you need to build an application that must support
n number of objects, you need to make sure
the code will work in many different cases.

In the case of 100 balls bouncing on the canvas, we will need to
create a ball object with a few more properties. Recall that the ball
object we created previously had only x and y
properties, and looked like this:

var ball = {x:p1.x, y:p1.y};

All the other variables that represented the ball (speed, angle, xunits, yunits) were global in scope to the canvasApp(). We used global variables because
we could get away with it. However, because we need to make sure
everything works the same way in this app, we make all those values
properties of each ball object.

For the multiple-ball-bounce application, we will create an object
that holds all the pertinent information about each bouncing ball:
x, y, speed,
angle, xunits, and yunits. Because we are going to create 100
balls of various sizes, we also add a property named radius, which represents the size of the ball
(well, half the size since it is a radius):

Next, in canvasApp(), we
iterate through a loop to create all the ball objects. Notice how
tempX and tempY are created below. These values
represent the ball’s starting location on the canvas. We create a random
value for each, but we offset it by the size of the ball (tempRadius*2). If we did not do that, some of
the balls would get “stuck” in a wall when the app starts because their
x or y location would be “through” the wall, but
their speed would not be enough so
that a “bounce” would get them back on the playfield. They would be
stuck in bouncing limbo forever (which is kind of sad when you think
about it).

Note

When you try this app, you will see that occasionally a ball
still gets stuck in a wall. There is a further optimization we need to
make to prevent this, but it is a bigger subject than this little
iteration. We will talk about it in the section Multiple Balls Bouncing and Colliding.

The tempSpeed variable is
created by subtracting the value of tempRadius from the value of maxSpeed, which we created earlier. The
speed is not random, but it is
inversely proportional to the size (radius) of the ball. A larger ball
has larger radius, so the value you subtract from tempSpeed will be larger, thus making the ball
move slower:

Note

When you run CH5EX4.html in
your web browser, you will notice that this little trick makes the
ball appear more “real” because your brain expects larger objects to
move slower.

Now we need to draw the balls onto the canvas. Inside drawScreen(), the code to draw the balls
should look very familiar because it is essentially the same code we
used for one ball in Example 5-4. We just need to
loop through the balls array to
render each ball object:

When you load Example 5-5 in your web
browser, you will see a bunch of balls all moving around the screen
independently, as shown in Figure 5-6. For the fun of
it, why not change the numBalls
variable to 500 or 1,000? What does the canvas look like then?

Multiple Balls Bouncing with a Dynamically Resized Canvas

Before we move on to some more complex interaction among balls,
let’s try one more thing. Back in Chapter 3, we resized the canvas with some
HTML5 form controls to display text in the center of the canvas. Well,
let’s do the same thing now with the ball example. This will give you a
better idea of how we can make objects interact with a dynamically
resizing canvas.

First, in the HTML, we create two HTML5 range controls, one for width and one for height, and set their maximum values to
1000. We will use these controls to
set the width and height of the canvas at runtime:

The event handler functions capture the changes to the range, set
theCanvas.width or theCanvas.height, and then call drawScreen() to render the new size. Without a
call to drawScreen() here, the canvas
will blink when the new size is applied in drawScreen() on the next interval:

Note

One last thing—let’s increase the number of balls set in canvasApp() to 500:

var numBalls = 500 ;

Now, check out Example 5-6 (CH5EX6.html from the code distribution). When
you run the code in a web browser, you should see 500 balls bounce
around the canvas, as shown in Figure 5-7. When you increase
the width or height using the range
controls, they continue moving until they hit the new edge of the
canvas. If you make the canvas smaller, the balls will be contained
within the smaller space. If you adjust the size too rapidly, some balls
will be lost off the canvas, but they will reappear when the canvas is
resized. Neat, huh?

Figure 5-7. Multiple balls bouncing while the canvas is resized on the
fly

Multiple Balls Bouncing and Colliding

Now it’s time to step it up again. Testing balls bouncing off
walls is one thing, but what about balls bouncing off one another? We
will need to add some pretty intricate code to handle this type of
interaction.

Ball interactions in physics

For this example, we are going to create an elastic
collision, which means that the total kinetic energy of the
objects is the same before and after the collision. This is known as
the law of conservation of momentum (Newton’s
third law). To do this, we will take the x and
y velocities of two colliding balls, and draw a
“line of action” between their centers. This is illustrated in Figure 5-8, which has been
adapted from Jobe Makar and Ben Winiarczyk’s Macromedia’s
Flash MX 2004 Game Design Demystified (Macromedia Press).
Then we will create new x and y velocities for each ball based on this
angle and the law of conservation of momentum.

To properly calculate conservation of momentum when balls
collide on the canvas, we need to add a new property: mass. Mass is the measurement of how much a
ball (or any object) resists any change in its velocity. Because
collisions tend to change the velocity of objects, this is an
important addition to the ball objects we will use on the
canvas.

Figure 5-8. Two balls colliding at different angles with a line of action
drawn between them

Making sure the balls don’t start on top of each other

We will work from the code we created for Example 5-6 (CH5EX6.html). The first big change to that
code is to make sure the balls don’t randomly start on top of one
another. If we let them start in the same location, they would be
forever intertwined and would spin off into oblivion. To be honest, it
looks pretty cool when that happens, but that’s not the result we are
looking to achieve.

In canvasApp(), we set a
variable named tempRadius to
5. We will use this value as the
radius for each ball we create. Next, we create another new variable
named placeOK and set it to
false. When this is equal to
true, we know we have found a place
to put a ball that is not on top of another ball.

Next, we enter a while() loop
that will continue to iterate as long as placeOK is false. Then, we set all the values for our
new ball object:

Now, we need to make a dynamic object out of the values we just
created and place that object into the tempBall variable. This is where we create a
mass property for each ball. Again,
we do this so that we can calculate the effect when the balls hit one
another. For all the balls in this example, the mass will be the same—the value of tempRadius. We do this because, in our 2D
environment, the relative size of each ball is a very simple way to
create a value for mass. Since the
mass and speed of each ball will be the same, they
will affect each other in a similar way. Later, we will show you what
happens when we create ball objects with different mass values.

Finally, we create nextX and
nextY properties that are equal to
x and y. We will use these values as “look ahead”
properties to help alleviate collisions that occur “between” our
iterations, which lead to overlapping balls and other oddities:

Now that we have our new dynamic ball object represented by the
tempBall variable, we will test to
see whether it can be placed at the tempX and tempY we randomly created for it. We will do this with a call to a new
function named canStartHere(). If
canStartHere() returns
true, we drop out of the while() loop; if not, we start all over
again:

placeOK = canStartHere(tempBall);
}

The canStartHere() function
is very simple. It looks through the ball array, testing the new tempBall against all existing balls to see
whether they overlap. If they do, the function returns false; if not, it returns true. To test the overlap, we have created
another new function: hitTestCircle():

Circle collision detection

The hitTestCircle() function
performs a circle/circle collision-detection test to see whether the
two circles (each representing a ball) passed as parameters to the
function are touching. Because we have been tracking the balls by the
center x and y of their location, this is quite easy to
calculate. First, the function finds the distance of the line that
connects the center of each circle. We do this using our old friend
the Pythagorean theorem
(A2+B2 =
C2). We use the nextx and nexty properties of the ball because we want
to test the collision before it occurs. (Again, if we test after by
using the current x and y locations, there is a good chance the
balls will get stuck together and spin out of control.) We then
compare that distance value to the
sum of the radius of each ball. If the distance is less than or equal
to the sum of the radii, we have a collision. This is a very simple
and efficient way to test collisions, and it works especially well
with collisions among balls in 2D:

Separating the code in drawScreen()

The next thing we want to do is simplify drawScreen() by separating the code into
controllable functions. The idea here is that to test collisions
correctly, we need to make sure some of our calculations are done in a
particular order. We like to call this an
update-collide-render cycle.

update()

Sets the nextx and
nexty properties of all the
balls in the balls
array.

testWalls()

Tests to see whether the balls have hit one of the
walls.

collide()

Tests collisions among balls. If the balls collide,
updates nextx and nexty.

render()

Makes the x and
y properties for each ball
equal to nextx and nexty respectively, and then draws
them to the canvas.

Updating positions of objects

The update() function loops
through all the balls in the balls
array, and updates the nextx and
nexty properties with the x and y
velocity for each ball. We don’t directly update x and y
here, because we want to test collisions against walls and other balls
before they occur. We will use the nextx and nexty properties for this purpose:

Better interaction with the walls

We discussed the interactions between balls and walls in the
last example, but there is still one issue. Since we move the balls by
the x and y location of their center, the balls would
move halfway off the canvas before a bounce occurred. To fix this, we
add or subtract the radius of the
ball object, depending on which
walls we are testing. For the right side and bottom of the canvas, we
add the radius of the ball when we test the walls. In this way, the
ball will appear to bounce exactly when its edge hits a wall.
Similarly, we subtract the radius when we test the left side and the
top of the canvas, so that the ball does not move off the side before
we make it bounce off a wall:

Collisions with balls

The collide() function tests
to see whether any balls have hit one another. This function uses two
nested loops, both iterating through the balls array to ensure we test each ball
against every other ball. We take the ball from the first loop of the
balls array, and place it into the
ball variable. Then we loop through
balls again, placing each ball in
the testBall variable, one at a
time. When we have both ball and
testBall, we make sure they are not
equal to one another. We do this because a ball will always have a
false positive collision if we test it against itself. When we are
sure they are not the same ball, we call hitTestCircle() to test for a collision. If
we find one, we call collideBalls(), and then all hell breaks
loose. (OK, not really, but the balls do collide, and some really
interesting code gets executed.) See that code here:

Ball collisions in depth

So now we get to the most interesting code of this example. We
are going to update the properties of each ball so they appear to
bounce off one another. Recall that we use the nextx
andnexty properties
because we want to make sure to test where the balls will be after
they are drawn—not where they are right now. This helps keep the
balls from overlapping in a way
that will make them stick together.

Note

Sometimes the balls will still stick together. This is a
common problem when creating collisions among balls. This happens
when balls overlap one another before the collision test, and the
reaction bounce is not enough to split them apart completely. We
have made every attempt to optimize this function for the canvas,
but we are sure further optimizations are possible.

The collideBalls() function
takes two parameters: ball1 and
ball2. Both parameters are the
ball objects that we want to make
collide:

function collideBalls(ball1,ball2) {

First, we need to calculate the difference between the center
points of each ball. We store this as dx and dy
(difference x and difference y). This should look familiar because we
did something similar when we tested for a collision between the
balls. The difference is that now we know they have collided, and we
want to know how that collision occurred:

To do this, we need to find the angle of the collision using the
Math.atan2() function. This
function gives us the angle in radians of the collisions between the
two balls. This is the line of action or angle of collision. We need
this value so that we can determine how the balls will react when they
collide:

var collisionAngle = Math.atan2(dy, dx);

Next, we calculate the velocity vector for each ball given the
x and y velocities that existed before the
collision occurred:

Next, we need to rotate the vectors counterclockwise so that we
can plug those values into the equation for conservation of momentum.
Basically, we are taking the angle of collision and making it flat so
we can bounce the balls, similar to how we bounced balls off the sides
of the canvas:

Note

If this is confusing to you, you are not alone. It took some
serious effort for us to translate this code from other sources into
a working example on HTML5 Canvas. The code here is based on
“Flash Lite Effort - Embedded Systems and Pervasive
Computing Lab” by Felipe Sampaio, available here: http://wiki.forum.nokia.com/index.php/Collision_for_Balls.
It is also partly based on Jobe Makar and Ben Winiarczyk’s work in
Macromedia Flash MX
2004 Game Design Demystified, and Keith Peters’ books on
ActionScript animation.

Now, when you execute Example 5-7 (CH5EX7.html), you will see a bunch of balls
of the same size and mass bumping off of each other and the walls of
the canvas, as shown in Figure 5-10. When you look at
this demo, imagine all the ways you could modify it to do different
things. You could create balls with different masses and different
speeds, or even create balls that don’t move but simply alter the
direction of other balls that hit them. In Example 5-8, we will take a slightly different
look at this same code and add some new properties to make it more
interesting.

Figure 5-10. Balls of the same size bouncing off one another

Multiple Balls Bouncing with Friction

If we want the balls to slow down and eventually stop, we need to
add friction to Example 5-7. For
our purposes, simple friction is a value we use to modify the velocity
of our objects every time they are drawn to the canvas.

In canvasApp(), we now want to
create balls of various sizes. In the previous example, the balls were
all the same size. It worked, but having balls of different sizes with
different masses will create more interesting effects. To do this, we
set minSize to
3 and maxSize to 12, meaning the radii for our balls will range
from 3 to 12 pixels. We also add a new property named
friction. This is a global property,
so it will not be applied to each individual ball. We set it to .01, which means our balls will degrade their
x and y velocities by .01 pixels per frame (every time drawScreen() is called):

We will now allow for various ball sizes. The mass of each ball
will be different, and balls will have different effects on one another
depending on their sizes. Recall that in Example 5-7 we needed a mass property so we could calculate
conservation of momentum when the balls collided. We are doing the same
thing here, but now the masses are different depending on the
size:

In update(), we apply the
friction value by calculating the
product of the current velocity multiplied by friction, and then
subtracting that value from the current velocity. We do this for both
the x and y velocities. Why must we do this instead of
simply subtracting the friction value from the x and y
velocities? Because the x and
y velocities are not always
proportional to each other. If we simply subtract the friction, we may
alter the velocity unintentionally. Instead, we need to subtract a value
for the friction that is proportional to the velocity itself, and that
value is the product of the velocity multiplied by the friction value. This method will give you a
smooth degradation of the velocity when the friction value is applied:

You can see the full version of this code by executing CH5EX8.html from the code distribution, or by
typing in Example 5-8. You should notice that
the smaller balls have less of an effect on the larger balls when they
collide, and vice versa. Also, the balls slow down as they move due to
the applied friction.