Predicting the future has always been a valued commodity ranging from foreseeing when a
drought would occur in agrarian times, to when a stock price will skyrocket in the digital
age. Physics is a fascinating science since it lets us do more than just predict the future,
we can actually determine the future. In Part 1 of this article we learned how to determine
the future of two masses moving toward each other. If we know the masses and velocities of two
blocks, we used an equation to solve for the velocities in the future (after the collision).
Before going any further, I strongly urge you read Part 1 of this article where we learned
about many things, including vectors, momentum, and conservation of momentum.
The 2D World
Part 1 of this article taught us the fundamentals of elastic collisions, but as I pointed out,
there were a few things that we still need to learn about. One of the most important issues is
dealing with the collision angle. The collision angle is the angle made by drawing a line
between the center of the two colliding objects (we'll only deal with round things in this
article). In a head-on collision the line connecting two objects is horizontal, so that
corresponds to an angle of zero degrees. That is rarely the case in the real world. Three
cases of colliding objects are shown in the simulation below (think of the simulation as
viewing billiard balls from above the table, with the red line showing the current angle
between the objects). In Case 1 the blue ball will barely graze the green one, in Case 2 the
blue one will strike a bit more cleanly, and in Case 3 you have a head on collision. Before
starting each simulation, can you guess what the result might be? Thinking about what happens
when playing pool can lead to great insight.
Were your guesses correct? If not, you probably haven't played enough pool :) With our code
from the last article, all three cases would have yielded results identical to Case 3, since
we never took the angle of the collision into account (did you notice the varying angles of
the red line at the time of the collisions).
Enough concepts, show me the code!
We'll start out with the same code we used in Part 1 of the article, but as we have just seen
the angle between the two particles must be taken into account when trying to determine the
final velocities. To calculate that angle we will use the position information of each
particle, so two lines are added to the reportInformation handler of the collision behavior:
add tempList, x
add tempList, y
Since this information is then passed along to the dealWithCollision handler (the handler that
determines the final velocities) I add several lines to pull the new information out of the
list
set x1 = getat(infoList1,4)
set y1 = getat(infoList1,5)
set x2 = getat(infoList2,4)
set y2 = getat(infoList2,5)
and then proceed to find the collision angle (often called phi) using the arc tangent function
set dx = x2-x1
set dy = y2-y1
if dx = 0. then
set phi = pi()/2.
else
set phi = atan(dy/dx)
end if
Since pictures are often worth a thousand words I present Figure 1 which shows two particles
at the time of collision. The angle names used in the Lingo are show in terms of the Greek
symbols used in the figure.
At this point, we have the x- and y- velocity components of each puck, but we will also need
the magnitude of the velocity of each puck (shown as the velocity vectors in Figure 1), and
the angle of motion (relative to the standard x-y axes) for each puck. I add several more
lines of code to the dealWithCollision handler to calculate these values:
set term = pi()/180.
-- used for angle to radian conversions
set v1i = sqrt(v1ix*v1ix+v1iy*v1iy)
set v2i = sqrt(v2ix*v2ix+v2iy*v2iy)
set ang1 = findAnAngle(v1ix,v1iy)*term
set ang2 = findAnAngle(v2ix,v2iy)*term
where the findAnAngle handler returns the angle (in degrees) by using the x and y velocity
values:
on findAnAngle xthing,ything
-- very basic angle finder..returns value in degrees
-- my mind likes degrees, Director likes radians
set term = pi()/180.
if xthing < 0. then
set t = 180.+ atan(ything/xthing)/term
else if xthing > 0. and ything >= 0. then
set t = atan(ything/xthing)/term
else if xthing > 0. and ything < 0. then
set t = 360.+atan(ything/xthing)/term
else if xthing = 0. and ything = 0. then
set t=0.
else if xthing = 0. and ything >= 0. then
set t = 90.
else
set t = 270.
end if
return t
end
Angles, angles, everywhere....
Why have we started finding all these angles? When I sat down to write my first collision code
I was a bit confused as to how to calculate the velocities after the collision. 1D is
relatively simple (after years of graduate school), but textbooks seemed to gloss over the 2D
material, and most problems provided so much additional information that going through the
full algebraic solution was never required. I finally found the answer in a used textbook
which I picked up for a dime. In 1954 Robert Becker 1 said, "Newton's Rule (i.e. the
conservation of momentum) applies to the components of velocity resolved along the common
normal surfaces of the colliding bodies at the point of contact. In the case of the two
spheres the velocity components involved are the components resolved along the line of centers
during the contact. Consequently, the components of velocity perpendicular to the line of
centers will be unchanged during the impact."
That statement was the best thing I had seen since sliced bread (although you can see why many
consider physics a tad dreary)! Here is my translation: If you view the collision along the
line between the two spheres, the velocities along that line will undergo momentum
conservation (the same way we calculated the x-components in Part 1), and the velocities
perpendicular to that line won't change. Although my words may seem as confusing as Becker's,
I have the power of multimedia to show you what I mean, along with the snippets of code (from
the dealWithCollision handler) applicable to each picture.
We first want to change our mind set from the standard x-y reference frame, to the new
reference frame where the x-axis lies along the collision line, and the y-axis is
perpendicular to that. The figure shows the new vector components,
and the corresponding code is given by:
-- find the velocities in the new coordinate system
set v1xr = v1i*cos((ang1-phi))
set v1yr = v1i*sin((ang1-phi))
set v2xr = v2i*cos((ang2-phi))
set v2yr = v2i*sin((ang2-phi))
We now use the conservation of momentum to determine the new x velocities in our new reference
frame, and the y-components do not change. The figure shows the 'flip-flop' of the
x-velocities,
and you can see that the code sets the final y velocities equal to the initial values after
calculating the final x velocities.
-- find the final velocities in the normal reference frame
-- the x velocities will obey the rules for a 1-D collision
set v1fxr = ((m1-m2)*v1xr+(m2+m2)*v2xr)/(m1+m2)
set v2fxr = ((m1+m1)*v1xr+(m2-m1)*v2xr)/(m1+m2)
-- the y velocities will not be changed
set v1fyr = v1yr
set v2fyr = v2yr
We now have the 'after collision' velocities, but we have to transform the components back to
the standard x-y reference frame. In this particular case, the velocity of the red puck was
entirely in the standard y-direction (so our x-velocity in the standard reference frame is
zero).
The relevant code (once again) is very dependent on angles!
-- convert back to the standard x,y coordinates
set v1fx = cos(phi)*v1fxr+cos(phi+pi()/2)*v1fyr
set v1fy = sin(phi)*v1fxr+sin(phi+pi()/2)*v1fyr
set v2fx = cos(phi)*v2fxr+cos(phi+pi()/2)*v2fyr
set v2fy = sin(phi)*v2fxr+sin(phi+pi()/2)*v2fyr
We've now found the velocity of each particle after a collision has occurred! As you could
see, angles (along with sines and cosines of many angles) played an important role in the
calculation (for learning more about angles stop by ExploreMath.com). With our new and
improved collision code things are looking pretty good, but one problem still remains - that
of the collision detection (i.e. when exactly should we make the call to the dealWithCollision
handler).
Collision Detection
In the previous article we simply looked for the distance between the two objects whenever we
step forward in time. That works equally well in this case, but what happens when particles
are moving rather quickly or slowly? Two things often happen when just checking for the
distances: 1) the particles seem to get stuck together like two things connected by a rubber
band (an "internal collision"), and 2) the particles are moving rather quickly and seem to
pass through each other on the screen without actually colliding.
The first case is due to particles which have slow velocities after a collision (in your code
they are still close together after the collision), and the second case is due to the fact
that you are not checking for separation distances frequently enough. I'll present two
solutions that I have used, and one more which I haven't yet tried, but it should work
(consider it a homework problem).
In order to deal with internal collisions a frame script can be used to check for collisions
(by looking for the distance between two particles) on each exitFrame, deal with the collision
when particles first overlap, and then keep track of which particles have not yet separated.
The first line of my frame script has a list properties (I tend to avoid globals...but they
could just as well be defined elsewhere via globals).
The particleSpriteList is a list that contains all the sprites that have the collision script
attached to them, and the collidingParticleList will always keep track of which particles are
currently in contact. The numberOfParticles property is just the total number of sprites with
the collision behavior. On every exitFrame we will now run through all particles with a repeat
loop to see who is touching whom.
I hard code my test distance here (24). You could also use the width of the sprites
when you have particles of varying widths. If the two particles were in the collidingList
then remove the pair.
on exitFrame
repeat with i = 1 to numberOfParticles-1
repeat with j = i + 1 to numberOfParticles
set particleA = getat(particleSpriteList,i)
set particleB = getat(particleSpriteList,j)
-- we now have selected two particles
-- to see if they are colliding
if distance(particleA, particleB) > 24. then
if getOne(collidingParticleList,[particleA,particleB]) ¬
<> 0 then
deleteAt collidingParticleList, getOne( ¬
collidingParticleList, [particleA,particleB])
end if
else
-- if the pair is not in the collidingList, add the
-- pair and call the collision script
if getOne(collidingParticleList,[particleA,particleB]) ¬
= 0 then
add collidingParticleList, [particleA,particleB]
dealWithCollision(particleA,particleB)
end if
end if
end repeat
end repeat
go to the frame
end
For most cases that script seems to work rather well when particles are moving with relatively slow
velocities, but as things begin to move more quickly a few more lines of code are worth
looking at.
Another method I have used (to avoid missed collisions) involves additional changes. In the
collision behavior I select a very small time step (e.g. set dTime to be 0.01), and rather
than just moving the particle once in the exitFrame handler of the collision behavior, I run a
repeat loop over small time increments before the position is actually changed on the computer
screen. The code is now written as...
on exitFrame me
if myActivity = TRUE then
repeat with i = 1 to 10
checkForCollision spriteNum
-- this will see if I have hit any other sprites
end repeat
set the loc of sprite spriteNum = point(x,y)
end if
end
The upper limit of the repat loop (10) can be changed to a higher or lower value.
The higher the value the less likely you are to miss a collision, but the code starts
to take far longer on the "processing" side.
I rewrite my frame script to a basic on exitFrame:go to the frame statement, and convert that
frame script (the full one above which contained two repeat loops) to a movie handler:
on checkForCollision aNum
repeat with j = 1 to numberOfParticles
set A = aNum
set B = getat(particleSpriteList,j)
particleA = min(A, B)
-- need to sort these to avoid (3,2) vs (2,3) collisions
particleB = max(A, B)
if particleA <> particleB then
if distance(particleA, particleB) > 24. then
if getOne(collidingParticleList,[particleA,particleB]) ¬
<> 0 then
deleteAt collidingParticleList, getOne( ¬
collidingParticleList,[particleA,particleB])
end if
else
if getOne(collidingParticleList,[particleA,particleB]) ¬
= 0 then
add collidingParticleList, [particleA,particleB]
dealWithCollision(particleA,particleB)
end if
end if
end if
end repeat
end
We only have one repeat loop at this point (rather than the two in the initial script), but
every sprite with the collision behavior calls this on an exitFrame. Also, with this script
the particle will often seem to collide without having touched another particle, since so many
calculations are performed before the stage is refreshed. Adjusting the dTime and the maximum
number of the repeat loop in the collision behavior often lead to a reasonable balance
depending on the initial velocities of your particles.
And for homework...
That is the end of the "tested" material that I use for a lot of my fun stuff, but one other
technique (which I've never actually found enough time to test), is to solve for the time at
which a collision (tc)between two particles will occur. If you run this test initially (for
all particles) and find the shortest time, you could then wait until that time arrives (while
particles continue to move at a constant velocity), deal with that collision and perform
calculations to find the next time a collision will occur, sit around waiting for the next
collision time to occur, etc. The figure shows two particles at a chosen time of t = 0, and
the time when a collision would occur.
In the figure above it is easy to look at the initial positions and velocities and realize the
two particles will collide, but trying to determine that fact mathematically is a bit more
difficult. The collision time for a pair of particles (with no acceleration) can be written as
where the variables are defined as,
The 1 and 2 subscripts refer to each individual particle, and the 'd' term is the distance
between the two particles when the collision occurs (see figure). The equation for the time of
the collision only gives a real solution if
so you can see a bit of testing is required when using this method.
The real world
Simulating the physical world with a computer language is always a challenge, but when the
final product can be used in the classroom and let that 'virtual light bulb' appear over the
students, it is worth the effort. This article discussed the techniques that I routinely use,
but there are many other methods that could be used for collisional coding. I also didn't
discuss frictional forces acting between particles, rotational motion due to the friction, and
a variety of other 'annoyances' that occur in the real world.
Sample movies are available for download in a Mac or PC format. These are Director 7 files.
1 Robert A. Becker, Introduction to Theoretical Mechanics, McGraw-Hill Book Company, 1954.
Raman Pfaff earned his Ph.D. in nuclear physics, spent some time as a professor, but is now
having fun as Director of Multimedia Development for 3MP in Charlottesville, VA. In a recent
dream sequence he was stuck in an infinite repeat loop but decided to see a movie, grab a
latte, and watch a Redwings game instead. He can be reached at pfaff@explorescience.com. Feel
free to learn a bit more at http://www.explorescience.com/
and http://www.exploremath.com/