I have a a program where circles can bounce into one another. I followed the directions from here for rotating the vectors and scaling the magnitudes based on the collision angle: http://www.vobarian.com/collisions/2dcollisions2.pdf
I wrote this code in python (the 0 index indicates the x coordinate):
norm_vect = [(object2.pos[0] - object1.pos[0]), (object2.pos[1] - object1.pos[1])]
unit = sqrt((norm_vect[0]**2) + (norm_vect[1]**2))
unit_vect = [float(norm_vect[0]) / unit, float(norm_vect[1]) /unit]
tan_vect = [-unit_vect[1], unit_vect[0]]
vel1 = object1.vel
vel2 = object2.vel
vel1_norm = vel1[0] * unit_vect[0] + vel1[1] * unit_vect[1]
vel1_tan = vel1[0] * tan_vect[0] + vel1[1] * tan_vect[1]
vel2_norm = vel2[0] * unit_vect[0] + vel2[1] * unit_vect[1]
vel2_tan = vel2[0] * tan_vect[0] + vel2[1] * tan_vect[1]
new_vel1_norm = (vel1_norm * (object1.mass - object2.mass) + 2 * object2.mass * vel2_norm) / (object1.mass + object2.mass)
new_vel2_norm = (vel2_norm * (object2.mass - object1.mass) + 2 * object1.mass * vel1_norm) / (object1.mass + object2.mass)
new_norm_vect1 = [new_vel1_norm * float(unit_vect[0]), new_vel1_norm * float(unit_vect[1])]
new_norm_vect2 = [new_vel2_norm * float(unit_vect[0]), new_vel2_norm * float(unit_vect[1])]
new_tan_vect1 = [new_vel1_norm * float(tan_vect[0]), new_vel1_norm * float(tan_vect[1])]
new_tan_vect2 = [new_vel2_norm * float(tan_vect[0]), new_vel2_norm * float(tan_vect[1])]
# Now update the object's velocity
object1.vel = [new_norm_vect1[0] + new_tan_vect1[0], + new_norm_vect1[1] + new_tan_vect1[1]]
object2.vel = [new_norm_vect2[0] + new_tan_vect2[0], + new_norm_vect2[1] + new_tan_vect2[1]]
The problem is that it works sometimes, but not othertimes. Can anyone tell me why? It seems like if the balls collide at the right angle then their exit trajectories swap or something. I wrote this in codeskulptor browser: http://www.codeskulptor.org/#user39_8q0Xdp3Y4s_2.py
Can anyone point out where I went wrong?
EDIT: Could it be the way that I process the collision? Here is the steps:
1) Draw the balls on the screen
2) Create set of unique pairs of collidable objects
3) For each ball, move the ball's position 1 frame forward according to the velocity:
->1) Check to see if the ball is hitting a wall
->2) For each pairset, if the ball in question is a member of the pair:
-->1) If distance between centers is less than sum of radii:
-->1) Calculate rebound trajectories
---2) Find N such that position + rebound trajectory *N is out of collision zone
The online simulation is really cool! I did not study your complete code in detail, just the snippet you posted in your question. From a quick glance, you correctly compute the tangential and normal unit vectors, the old normal and tangential velocities, and the new normal velocity. But after that, you seem to get lost a bit. As explained in the document about the collisions, the tangential velocities do not change during the collision, so there is no need to calculate new_tan_vect1/2
. I also don't understand why you are calculating new_norm_vect1
, the normal vector does not change during the collision.
Some other small remarks:
why do you use float()
all over your code? This is normally not needed. If the reason for this is to get correct results for division, you should really add a from __future__ import division
at the top of your code, since you seem to be using Python2. See this old question for more info.
What you call norm_vect
is actually the un-normalized normal vector, and what you call unit_vect
is actually the normalized normal unit vector. I would just call both norm_vect, to make the difference between normal and tangential more clear. A unit vector is any vector with length 1, so using that for the normal vector is a bit misleading.
If you are planning to do more of these kind of simulations, you should consider learning about numpy
. This allows you to write vectorized calculations, instead of writing out all the equations for x
and y
by hand. E.g. norm_vect = pos2 - pos1; norm_vect /= np.linalg.norm(norm_vect)
or object1.vel = norm_vect * new_vel1_norm + tang_vect * vel1_tang
.
I would write your snippet should more or less like this (untested code):
from __future__ import division # move this to the top of your program
# calculate normal and tangential unit vectors
norm_vect = [(object2.pos[0] - object1.pos[0]),
(object2.pos[1] - object1.pos[1])] # stil un-normalized!
norm_length = sqrt((norm_vect[0]**2) + (norm_vect[1]**2))
norm_vect = [norm_vect[0] / norm_length,
norm_vect[1] / norm_length] # do normalization
tang_vect = [-norm_vect[1], norm_vect[0]] # rotate norm_vect by 90 degrees
# normal and tangential velocities before collision
vel1 = object1.vel
vel2 = object2.vel
vel1_norm = vel1[0] * norm_vect[0] + vel1[1] * norm_vect[1]
vel1_tang = vel1[0] * tang_vect[0] + vel1[1] * tang_vect[1]
vel2_norm = vel2[0] * norm_vect[0] + vel2[1] * norm_vect[1]
vel2_tang = vel2[0] * tang_vect[0] + vel2[1] * tang_vect[1]
# calculate velocities after collision
new_vel1_norm = (vel1_norm * (object1.mass - object2.mass)
+ 2 * object2.mass * vel2_norm) / (object1.mass + object2.mass)
new_vel2_norm = (vel2_norm * (object2.mass - object1.mass)
+ 2 * object1.mass * vel1_norm) / (object1.mass + object2.mass)
# no need to calculate new_vel_tang, since it does not change
# Now update the object's velocity
object1.vel = [norm_vect[0] * new_vel1_norm + tang_vect[0] * vel1_tang,
norm_vect[1] * new_vel1_norm + tang_vect[1] * vel1_tang]
object2.vel = [norm_vect[0] * new_vel2_norm + tang_vect[0] * vel2_tang,
norm_vect[1] * new_vel2_norm + tang_vect[1] * vel2_tang]
I renamed some of the variables to make it more clear.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With