Circle Collision-Help Needed
Author |
Message |
zwnage
|
Posted: Sun Jan 14, 2007 9:48 pm Post subject: Circle Collision-Help Needed |
|
|
Hello. While looking up peoples sources to try and get ideas, I came across this neat snippet of code from a pool game:
code: |
angle := arctand ((ballY (k) - ballY (i)) / ((ballX (k) - ballX (i)))) % angle of hit
cosAngle := cosd (angle)
sinAngle := sind (angle)
speedAfterHit := (2 * ((ballXSpeed (i) * cosAngle + ballYSpeed (i) * sinAngle) - (ballXSpeed (k) * cosAngle + ballYSpeed (k) * sinAngle))) / (ballMass (i) + ballMass (k))
% new velocities of the balls
ballXSpeed (i) -= speedAfterHit * ballMass (k) * cosAngle
ballYSpeed (i) -= speedAfterHit * ballMass (k) * sinAngle
ballXSpeed (k) += speedAfterHit * ballMass (i) * cosAngle
ballYSpeed (k) += speedAfterHit * ballMass (i) * sinAngle
|
I tried a few test programs with that code, and it works a lot better than the code I'm trying to use, but I really want to learn how this code works, especially whats happening in speedAfterHit and why are we muiltiplying the trig ratios of the angle hit with the ball speeds. Can anyone please explain the code? Thanks. |
|
|
|
|
|
Sponsor Sponsor
|
|
|
Cervantes
|
Posted: Sun Jan 14, 2007 10:46 pm Post subject: Re: Circle Collision-Help Needed |
|
|
First, have you checked this out? Tutorial on Circular Collision data. I admit it isn't much of a tutorial, but it's something.
zynage wrote: why are we muiltiplying the trig ratios of the angle hit with the ball speeds
This is very common. See, using cosd gives you a number between 0 and 1 that represents the length of a horizontal line from the origin to directly below the point.
math: |
/|
/ |
/ | <-- this line is given from sin(theta) where theta is the angle from the horizontal line to the diagonal line
/ |
/ |
/_____|
^--- that line is given from cos(theta) where theta is the angle from that line to the diagonal line
|
So if the speed of a ball is 1, then use cos to get the horizontal speed of the ball. However, if the speed of the ball is 5, simply using cos won't work. We need to extend the length of that horizontal line by 5, because the diagonal line was extended by a factor of 5. So we'd do 5*cos(theta).
I'm not too sure what's going on in speedAfterHit. I'm a bit skeptical of this code because it seems to assuming that the speed of a ball divided by the mass of a ball is a constant number--speedAfterHit. That's clearly not true. Take two pool balls for example. If I shoot one ball directly at the other ball, (ignoring spin) the first ball will transfer 100% energy into the second ball, so the second ball will continue at the speed of the first ball and the first ball will come to a stop. This is because they have the same mass.
I suggest you try to work out the physics of this on paper. You'll need to use conservation of momentum and conservation of kinetic energy (assume this, unless you want inelastic collisions--ones where the balls stick together somewhat or deform slightly). I haven't actually worked out the math of this myself, but I think that'll be all you need in two dimensions. |
|
|
|
|
|
zwnage
|
Posted: Mon Jan 15, 2007 12:56 am Post subject: RE:Circle Collision-Help Needed |
|
|
Thanks for the help. I have tried to work this out on paper, but their code surpasses mine because the balls don't tend to overlap as much. I think I originally got it from thoughtful's tutorial.
Heres what I'm trying to use (pardon the lack of comments, I made it right now out of some notes I had on paper.)
code: |
setscreen ("offscreenonly")
var ballX, ballY : flexible array 1 .. 0 of real
var ballXSpeed, ballYSpeed : flexible array 1 .. 0 of real
var ballRadius : flexible array 1 .. 0 of int
var ballAmount : int := 3
var ballVelocity, ballVelocity2, ballNewVelocity, ballNewVelocity2, ballVelocityAngle, ballVelocityAngle2, ballNewAngle, ballNewAngle2 : real := 0
for i : 1 .. ballAmount
new ballX, upper (ballX) + 1
new ballY, upper (ballY) + 1
new ballXSpeed, upper (ballXSpeed) + 1
new ballYSpeed, upper (ballYSpeed) + 1
new ballRadius, upper (ballRadius) + 1
ballRadius (i) := Rand.Int (30, 50)
ballX (i) := Rand.Int (ballRadius (i), maxx - ballRadius (i))
ballY (i) := Rand.Int (ballRadius (i), maxy - ballRadius (i))
ballXSpeed (i) := Rand.Int (1, 2)
ballYSpeed (i) := Rand.Int (1, 2)
end for
function distance (xInput1, yInput1, xInput2, yInput2 : real) : real % function that returns the distance between two points
result ((xInput2 - xInput1) ** 2 + (yInput2 - yInput1) ** 2) ** 0.5
end distance
loop
for i : 1 .. ballAmount
for j : 1 .. ballAmount
if i ~= j then
if distance (ballX (i), ballY (i), ballX (j), ballY (j)) <= ballRadius (i) + ballRadius (j) then
ballVelocity := ((ballXSpeed (i) ** 2 + ballYSpeed (i) ** 2) ** 0.5)
ballVelocity2 := ((ballXSpeed (j) ** 2 + ballYSpeed (j) ** 2) ** 0.5)
ballVelocityAngle := arctand (ballYSpeed (i) / ballXSpeed (i)) - arctand ((ballY (i) - ballY (j)) / (ballX (i) - ballX (j)))
ballVelocityAngle2 := arctand (ballYSpeed (j) / ballXSpeed (j)) - arctand ((ballY (i) - ballY (j)) / (ballX (i) - ballX (j)))
ballNewVelocity := ((((cosd (ballVelocityAngle)*ballVelocity - cosd (ballVelocityAngle2)*ballVelocity2) ** 2) + (sin (ballVelocityAngle)*ballVelocity) ** 2) ** 0.5)
ballNewVelocity2 := ((((cosd (ballVelocityAngle)*ballVelocity - cosd (ballVelocityAngle2)*ballVelocity2) ** 2) + (sin (ballVelocityAngle2)*ballVelocity2) ** 2) ** 0.5)
ballNewAngle := (arctand ((ballY (i) - ballY (j)) / (ballX (i) - ballX (j))) + arctand (ballVelocityAngle / ballNewVelocity))
ballNewAngle2 := (arctand ((ballY (i) - ballY (j)) / (ballX (i) - ballX (j))) + arctand (ballVelocityAngle2 / ballNewVelocity2))
ballXSpeed (i) := cos (ballNewAngle) * ballNewVelocity
ballYSpeed (i) := sin (ballNewAngle) * ballNewVelocity
ballXSpeed (j) := cos (ballNewAngle2) * ballNewVelocity2
ballYSpeed (j) := sin (ballNewAngle2) * ballNewVelocity2
ballX (i) += ballXSpeed (i)
ballY (i) += ballYSpeed (i)
ballX (j) += ballXSpeed (j)
ballY (j) += ballYSpeed (j)
end if
end if
end for
end for
for i : 1 .. ballAmount
if ballX (i) > maxx - ballRadius (i) or ballX (i) < ballRadius (i) then
ballXSpeed (i) *= -1
end if
if ballY (i) > maxy - ballRadius (i) or ballY (i) < ballRadius (i) then
ballYSpeed (i) *= -1
end if
ballX (i) += ballXSpeed (i)
ballY (i) += ballYSpeed (i)
Draw.Oval (round (ballX (i)), round (ballY (i)), ballRadius (i), ballRadius (i), black)
end for
delay (10)
View.Update
cls
end loop
|
Since I havn't taken physics yet (its next sem), I might have mixed up a few terms. The velocities might be called speed or momentem or something.
Heres the jist of it:
find a hit
find the distance traveled each frame by both the balls before the hit (speed or velocity or whatever you call it)---ie : ballVelocity := ((ballXSpeed (i) ** 2 + ballYSpeed (i) ** 2) ** 0.5)
find the angle the ball was originally travelling at. -- ie : ballVelocity := ((ballXSpeed (i) ** 2 + ballYSpeed (i) ** 2) ** 0.5)
calculate the new velocities by subtracting the balls velocity by the other balls velocity, thus slowing the ball down or reversing the direction.-- ie :ballNewVelocity := ((((cosd (ballVelocityAngle)*ballVelocity - cosd (ballVelocityAngle2)*ballVelocity2) ** 2) + (sin (ballVelocityAngle)*ballVelocity) ** 2) ** 0.5)
calculate the new angle of bounce
use the angle of bounce and the new velocites to get the x and y speeds.
Now this code is far from perfect. The program cannot account for when both balls goes hits while going in the same direction (ie : one is faster than the other and hits it from behind). The velocity minus part will slow one of the balls down like its supposed to, but the other ball is slowed too (and probably will go in the opposite direction, towards the first ball). This causes an overlap, and causes them to stick or fly away wildly. The solution I thought of is to give the velocites a positive or negative value, but that would mean I would have to divide my velocites into a further set: the x and y values, I think.....
I dunno if anyone can do anything with that snippet of code, but your welcome to try. |
|
|
|
|
|
Ultrahex
|
Posted: Mon Jan 15, 2007 8:53 am Post subject: Re: Circle Collision-Help Needed |
|
|
Just for your guys information, this is collision response, not collision detection correct?, so what you are trying to do is determine angular momentum, along with momentum, so you need to determine the new velocities along with the new direction.
For those who don't understand the collision detection:
The Collision of a Circle to a Circle is very simple, It is explained by the pictures below:
code: | type Ball :
record
x : real
y : real
v : real % magnitude of velocity
dir : real
r : real
end record
var balls : array 1 .. 2 of Ball
balls (1).x := 50.0
balls (1).y := 50.0
balls (1).v := 5
balls (1).dir := 180
balls (1).r := 40
balls (2).x := 150.0
balls (2).y := 150.0
balls (2).v := 5
balls (2).dir := 45
balls (2).r := 40
View.Set ("offscreenonly")
loop
% Update Position
for i : 1 .. upper (balls)
% Wall Collision
%---Horizontal
if (balls (i).x + balls (i).r + cosd (balls (i).dir) * balls (i).v) >= maxx or (balls (i).x - balls (i).r + cosd (balls (i).dir) * balls (i).v) < 0 then
balls (i).dir := -balls (i).dir + 180
put balls (i).dir
end if
%---Vertical
if (balls (i).y + balls (i).r + sind (balls (i).dir) * balls (i).v) > maxy or (balls (i).y - balls (i).r + sind (balls (i).dir) * balls (i).v) < 0 then
balls (i).dir := -balls (i).dir + 360
end if
% Update x/y positon based on magnitude of velocity
balls (i).x += cosd (balls (i).dir) * balls (i).v
balls (i).y += sind (balls (i).dir) * balls (i).v
end for
% Drawing
for i : 1 .. upper (balls)
drawoval (round (balls (1).x), round (balls (1).y), round (balls (1).r), round (balls (1).r), red)
drawoval (round (balls (2).x), round (balls (2).y), round (balls (2).r), round (balls (2).r), blue)
end for
% Circle-To-Circle Collison
if sqrt ((balls (1).x - balls (2).x) ** 2 + (balls (1).y - balls (2).y) ** 2) <= (balls (1).r + balls (2).r) then
% Collison Has Occured
put "Collision!"
View.Update()
delay(100)
end if
View.Update ()
delay (20)
cls
end loop |
|
|
|
|
|
|
zwnage
|
Posted: Mon Jan 15, 2007 11:45 pm Post subject: Re: Circle Collision-Help Needed |
|
|
Yes, I'm trying to find collision response, not collision detection. I did a little research overnight and found that the method I was trying to implement is based on vectors, yet I still have no idea how to fix it so that they don't stick so often. Is there an expample code with vectors out there?
And I'm still confused about the first piece of code. I looked over thoughtful's tutorial a few more times, but there wern't too many comments explaining the collision responce process. Anyone have any ideas? |
|
|
|
|
|
Skynet
|
Posted: Tue Jan 16, 2007 9:33 am Post subject: Re: Circle Collision-Help Needed |
|
|
Yeah, I figured out how their code works.
There are two major points:
1) All of those cosAngle and sinAngle terms are performing a rotation on the x and y axis. When you have a collision between two circles, force is only applied in the direction from one center to the other. (or, in more complicated terms, the normal of the surfaces at the point of contact) So, if you have objects moving in two dimensions, force is going to be applied in both the x and the y direction. The programmer redefines the x and y direction and solves the physics in only one dimension - simplifying the collision response to something that acts like two balls moving directly towards each other. That's easier to solve.
2) There's a generalization the programmer derived - "speedAfterHit". It's not a speed, it's...something else, with the units of m/(s * kg). I didn't find it until I actively tried to make my equations I found match the code you posted. It's a clean way of reducing the amount of work the computer has to do.
If you'd like, I can clean up my work and post it this evening.
For the benefit of anyone else reading this, concepts you should be familiar with:
Trigonometry
Vectors/'Components'
Momentum
Coefficient of Restitution |
|
|
|
|
|
zwnage
|
Posted: Tue Jan 16, 2007 4:47 pm Post subject: Re: Circle Collision-Help Needed |
|
|
Thank you for your help. The "speedAfterHit" was originally named something else, but I renamed it while I was testing the formula in my code. I guess "speed" is not really a good way of describing it.
After thinking your post over a bit, I think my approach to collision responce is simular to the method in question, but as you said, it is much simpler and faster than what I was doing. Oh, and after a bit more tinkering, I think I found my mistake in the code I posted. Instead of subtracting the vectors, I think I should switch them between the two balls. Hopefully, this is the proper way of doing it; I will try to impliment it as soon as I have the time. |
|
|
|
|
|
|
|