 A Flash Developer Resource Site

# Thread: Billiard Ball-like angle calculations

1. ## Billiard Ball-like angle calculations

I have this ball, right? And it's bouncing. It bounces around the stage without a care in the world... unless! Wait a minute! There's another ball bouncing around! What will happen when they collide?
...not what they should, that's what...

I have the detection of when they hit working fine (I've watched it time and time again; when the edges touch, they change direction)
I have the momentum fine (assuming no spin)

My problem is in the angles calculations. Here's what I have:

PHP Code:
``` function twoCircleCollision(circle1,circle2,circle1xSpd,circle1ySpd,circle2xSpd,circle2ySpd):void {     xSpdDif_orig = circle2xSpd-circle1xSpd     ySpdDif_orig = circle2ySpd-circle1ySpd     xdif_orig = circle2.x-circle1.x     ydif_orig = circle2.y-circle1.y     angleOfOrigMove2 = Math.atan2(ySpdDif_orig,xSpdDif_orig)     RadiansToDegrees(angleOfOrigMove2)     angleOfOrigMove2 = Angle_out     angleOfOrigMove1 = Math.atan2(-ySpdDif_orig,-xSpdDif_orig)     RadiansToDegrees(angleOfOrigMove1)     angleOfOrigMove1 = Angle_out     angleOfCentreToCentreFromHoriz2 = Math.atan2(ydif_orig,xdif_orig)     RadiansToDegrees(angleOfCentreToCentreFromHoriz2)     angleOfCentreToCentreFromHoriz2 = Angle_out     angleOfFinalMove2 = 2*angleOfCentreToCentreFromHoriz2 - angleOfOrigMove2      angleOfFinalMove1 = 2*angleOfCentreToCentreFromHoriz2 - angleOfOrigMove1     DegreesToRadians(angleOfFinalMove2)     angleOfFinalMove2 = Angle_out     DegreesToRadians(angleOfFinalMove1)     angleOfFinalMove1 = Angle_out     magOfDirect1 = Math.sqrt(circle2xSpd*circle2xSpd+circle2ySpd*circle2ySpd)     magOfDirect2 = Math.sqrt(circle1xSpd*circle1xSpd+circle1ySpd*circle1ySpd) } function RadiansToDegrees(TheAngle):void {     if (TheAngle < 0) {         TheAngle += 2*Math.PI     }     TheAngle = TheAngle*(180/Math.PI)     Angle_out = TheAngle } function DegreesToRadians(theAngle):void {     theAngle = theAngle*(Math.PI/180)     if (theAngle > Math.PI) {         theAngle -= 2*Math.PI     }     theAngle = Angle_out }  ```
I've tested the RadiansToDegrees and DegreesToRadians functions, so don't blame them. I just included them to show how I've got it set up (You can blame how I've used them, however)

After a lot of searching online the only way I have managed to understand calculating the angle after a collision is with the R = 2W - P formula proposed by Ed Mack (yeah, I'm sure all that projection stuff works, but this should work too... right?)

Anyway... I'm a bit sick of trying to work out what has gone wrong when I don't know if I'm even doing it right; so I'm hoping someone will be able to help me here...  Reply With Quote

2. how did you derive this code? there's some weird flow going on there with setting Angle_out instead of returning a value from your conversion functions--that sort of stuff makes determining the state messy. also, DegreesToRadians does nothing--I'm assuming you pasted incorrectly.

my suggestion is to let go of the trigonometric approach and switch to a vector-based solution. it's more intuitive, more computationally efficient and less error prone. for some reason certain people do not welcome this suggestion--if you're not one of those people, I'm happy to help you out with switching.  Reply With Quote

3. That weird flow is how my brains logic works.

But... Switching is good with me if it works. Care to explain the vectors, 'cause I can't make head nor tail of what I've found. I do know about vectors; but only from a Year 12 Physics view. (So I know that a vector is made up of both an x value and a y value, and a direction)
What's up with the dot product? I've only managed to see it in a strange manner where they have some property of a variable that I don't know; like 2v.vx
EDIT: I liked this website, but I couldn't understand it once it got into the useful stuff...
http://www.tonypa.pri.ee/vectors/start.html

As for why my code is so weird:
I don't know how to use "return" so I never have.

I thought trigonometry was the way to go 'cause I knew something of it, and... well... it makes sense to me... Vectors always seem a very round-about way to calculate things. More efficient, I'm sure, but like I said. I can't understand it, and trig has managed to get me this far...

I didn't paste incorrectly... And it does do something... it turns an angle thats in degrees into an angle that's in Flash's weird way of radians (where 181 degrees is a little bit more than -PI). EDIT: Maybe you haven't scrolled down in the PHP box?  Reply With Quote

4. Originally Posted by Multihunter I didn't paste incorrectly... And it does do something... it turns an angle thats in degrees into an angle that's in Flash's weird way of radians (where 181 degrees is a little bit more than -PI). EDIT: Maybe you haven't scrolled down in the PHP box?
It does nothing as is. No value that you're using is being set.

There's nothing magical about vectors, they're just a natural way of expressing things in any dimension. Adding 2 vectors together is the equivalent of "moving" by the amount depicted in each. So let's say you're following a treasure map that tells you to walk 10 paces to the right, then 5 paces up, then another 20 paces right, and another 3 paces up. You can write these directions as the vectors <10,5> and <20,3>. You could walk 10 units to the right and 5 up. Then, from that point, walk 20 units right again and 3 more up. What you've done, however, is waste time because you could have just added them together to find the end point relative to the beginning:
<10,5> + <20,3> = <30,8>.

The dot product of 2 vectors is the sum of the products of each corresponding element. For example, using the vectors above, the dot product would be:
10 * 20 + 5 * 3 = 215

To gain some intuition about this, consider the dot product of a vector, <x,y>, and itself: you have x^2 + y^2, which you should know from trigonometry as the squared length of a right triangle's hypotenuse. With this knowledge you can calculate the length of any vector, v:
SquareRoot( DotProduct( v, v ) )

You can always think of a vector as this hypotenuse, where the x and y components are the 2 perpendicular sides of the triangle.

There's also value in investigating the sign of the dot product of 2 vectors. If the dot product is 0, the vectors are perpendicular to each other. If the dot product is less than zero, then they point in opposite directions; and likewise, if it's greater than zero, they point in the same direction.

So vectors are a very natural way of representing the state of your physical system: position, velocity and acceleration are all vectors.

This is something I wrote a little while ago that does elastic collision response.
http://wonderfl.net/c/8RNL/

See the function "resolve" in the ParticleParticlePair class on line 359. This resolves a collision between 2 circles. It solves for an impulse that gets added to the particles' velocities. It is the magnitude of the integrated reaction force. Take a shot at translating your "twoCircleCollision" method into this form, and we'll take it from there.  Reply With Quote

5. I notice that I HAVE copied incorrectly. My apologies, the last part of the function is supposed to be reversed. (Angle_out = theAngle)

Also; I understand everything you just said (Mostly because I knew about adding vectors already). But not your code at all. And none of it gives me any clue as to how it helps, and how it is used to calculate collisions.  Reply With Quote

6. WAIT A SECOND! Are you using the dot product to calculate where it would be if it collided between frames?  Reply With Quote

7. without understanding it, it still should be easy to get working on your end. it's 6 lines of code to replace that whole function you have. but i'll explain the physics anyway.

it's not used to "calculate collisions." the function applies an impulse that will change each ball's velocity appropriately. to explain how i'm solving for the impulse, let's consider the simpler case of 2 particles colliding in a 1-dimensional universe--1 is traveling to the right and 2 is traveling to the left.

1 O--> <--O 2

we know from newton's 3rd law that each force is met with an equal and opposite force. it would be much more complicated to model collision response using forces, so we apply an impulse--a quantity used to cause an instantaneous change in velocity:

j = ( -( 1 + e ) * ( m1v1 - m2v2 ) ) / ( 1 / m1 + 1 / m2 )

where j is impulse, m is mass and v is velocity. e is what's called the coefficient of restitution, which is a fancy way of saying how bouncy or elastic something is. it ranges from 0 - 1, 0 being inelastic (like putty) and 1 being perfectly elastic (like a super ball). this looks a little scary, but it's really just giving us a scalar value that represents an exchange in momentum in the given direction (chosen by whether we use m1v1 - m2v2 or m2v2 - m1v1). the change in velocity from this impulse is then scaled by the inverse of each particle's mass:

v1 += j / m1
v2 -= j / m2

let's plug a few values in to see if this makes sense. first, let's say the mass of each particle is 1 and both particles are traveling at 10 meters per second (assuming perfect elasticity):

e = 1

v1 = 10
m1 = 1

v2 = -10
m2 = 1

j = ( -( 1 + e ) * ( 10 * 1 -( -10 * 1 ) ) ) / ( 1 + 1 )
j = ( -2 * 20 ) / 2 = -20

v1 += -20 / 1 = -10
v2 -= -20 / 1 = 10

we can see that, as expected v1 and v2 have completely flipped around and 1 is now traveling back to the left at 10 meters per second and 2 is traveling back to the right at 10 meters per second. it's easy to see if we change elasticity to 0, the resulting velocities for both particles will be 0.

that is the 1-dimensional version of what my method is doing in 2 dimensions. it's only slightly more complicated in 2D, though. basically we don't have the nicety of velocity being a scalar and so we have to find relative velocity along the line of contact (contact normal). the change in velocity also points along this normal, but everything else is the same.

hope that helps  Reply With Quote

8. That's a useful formula, it does help, but it doesn't explain how your 6 lines of coding works.
Sorry I'm not understanding; I shouldn't expect you to explain it all, I'm sure I would be able to get it given enough time researching it on the internet.

As a side note; I have made my trig method work. The WALLS angle is PERPENDICULAR to the angle between the two circles.  Reply With Quote

9. it explains line by line how it works and the physics behind it. if you don't understand something, you'll have to ask something specific about it.  Reply With Quote

10. So what you have in the "resolve" function is:
PHP Code:
``` var cn:Vec2D = w.normal; var dv:Vec2D = p.v; var impulse:Number = cn.dot( dv.times( -2 ) ) / cn.dot( cn.times( 1 / p.mass ) ); p.v.plusEquals( cn.times( impulse / p.mass ) );  ```
EDIT: What does it actually resolve, and what's the output?
What is "w.medium", and how is it calculated?
When is this function called?
How does it tell it which direction it needs to go?
Does your Vec2D class make it so that both the x and y are calculated using these formulae and then each stored as a property of the variables?

EDIT2: Damn you, time zones, making these awkward timings...  Reply With Quote

11. you're looking at the wrong resolve function--that handles collision between a particle and a wall. where i link to that, i say line 359, check that function instead.

it sounds like you're having more trouble with the programmatic end of things. every bit of that algorithm is either standard floating point mathematical operations or can be found elsewhere in the application code.

but anyway, i think you probably just need me to put it in the form of your original algorithm:

PHP Code:
``` function twoCircleCollision( circle1:DisplayObject, circle2:DisplayObject, circle1xSpd:Number, circle1ySpd:Number, circle2xSpd:Number, circle2ySpd:Number ) : void {    var dx:Number = circle1.x - circle2.x;   var dy:Number = circle1.y - circle2.y;   var length:Number = Math.sqrt( dx * dx + dy * dy );   dx /= length;   dy /= length;   var dvx:Number = circle1xSpd - circle2xSpd;   var dvy:Number = circle1ySpd - circle2ySpd;   var impulse:Number = -2 * ( dx * dvx + dy * dvy );   impulse /= ( 1 / mass1 + 1 / mass2 ); //you need to add these mass values   //these are your new velocities   var newCircle1xSpd:Number = circle1xSpd + dx * ( impulse / mass1 );   var newCircle1ySpd:Number = circle1ySpd + dy * ( impulse / mass1 );   var newCircle2xSpd:Number = circle2xSpd - dx * ( impulse / mass2 );   var newCircle2ySpd:Number = circle2ySpd - dy * ( impulse / mass2 );}  ```
obviously, i don't have your application and i have no idea if there are errors in this or not, but it should be right.  Reply With Quote

12. Ok, I now have a much better understand of where this is all coming from.
Thank you.

Just... I still can't quite figure out what you've done there...
When you normalised dx and dy, you made it so that the "new" length would be equal to 1?
So... why do you have it multiplying the sum of the inverse masses?
And why do you have newCircle1xSpd = circle1xSpd - dx*(impulse/mass1)? Shouldn't it be plus? As per your v1 += j / m1?
But, yeah, I see where it is all going now, which is awesome, thank you. (I also should have written it out on a piece of paper sooner, too)  Reply With Quote

13. When you normalised dx and dy, you made it so that the "new" length would be equal to 1?
yes, the normalization makes the vector's length 1--but it still points in the same direction: from circle 1's center to circle 2's.

So... why do you have it multiplying the sum of the inverse masses?
I have no idea why I'm doing that--that value will always be 1 and so it's a pointless calculation. I'll have to look through the rest of the code in the link I posted and see how I muffed that up!

And why do you have newCircle1xSpd = circle1xSpd - dx*(impulse/mass1)? Shouldn't it be plus? As per your v1 += j / m1?
Yes--it was an error as I wasn't able to test anything. Sorry!

I've corrected the post to reflect these issues.  Reply With Quote

14. Ok, nearly there (I think).

I worked out that according to that you had there;
Momentum of Circle1 - Momentum of Circle2 = dx*dvx + dy*dvy
How does that work?
EDIT2: Maybe that's the dot product? Even so... how does that work?

I also notice that dx and dy don't change... so wouldn't the gradient of the path also not change?

EDIT: I can't see where you use a dot product, either...  Reply With Quote

15. it is the dot product of the collision normal and the balls' relative velocities. it gives us the signed magnitude of the projection of the relative velocities onto the normal (since the normal has a length of 1).

i'm not exactly following the rest of your reply--what do you mean you can't see where i use the dot product?  Reply With Quote

16. Well that last bit was edited in before the "earlier" edit, hence the 2 next to it to indicate it came later.

If you look at the original formula you gave for impulse; j = ( -( 1 + e ) * ( m1v1 - m2v2 ) ) / ( 1 / m1 + 1 / m2 )
and the one you used in the code; (-2 * ( dx * dvx + dy * dvy ))/( 1 / mass1 + 1 / mass2 )

Assuming that e = 1, and since the dividing by the inverse masses is the same, that would mean that dx*dvx +dy*dvy would have to equal m1v1-m2v2.

I want to know how you managed to come up with dx*dvx + dy*dvy...

And I just realised what was confusing me about the dx and dy; because they were in the final velocity calculation, I confused them with the velocities of the circles.
Which leads me to my next question;
Why does the direction between the circles give the direction of each circle?

EDIT: My real problem is (I think), as suggested in the title, and has been for a while, where are you getting the new angle from? And how is it applied?
I get that you normalise to get the original "angles" and the impulse to determine how fast, but how is it that you can get a new "angle" (I use quotation marks so that it's clear that it's not actually an angle, more of a gradient of the path)

EDIT2: Also, I may be clearer in the morning, I have been staying up til 11pm (and getting up at 5:30am) every night this week trying to figure this stuff out. I probably would do better with more rest. But I feel I'm so close...

EDIT3: I was actually online at the same time as you, but I don't think you expected a response. I'll be on pretty much all of my tomorrow. So if you post anything while I'm awake, I will know, and respond as fast as I can.  Reply With Quote

17. Ok. We did a lot on graphs last year in Maths, so I decided to try once again to work it out with gradients, see if it looked like what the vectors were and see if I could link them or come up with another method. ('cause, again, I understood what I was doing with gradients)

They seem to be awfully similar to what you are trying to get me to understand with these vectors.

EDIT: Just to let you know that by "psuedo wall", I mean the instantaneous imaginary wall that exists between to colliding circles such that the reflected angle of the ball hitting it is the same as if it were hitting an actual wall at these particular angles.

I ended up working out that the gradient of the reflected balls path is reflected on the graph along the y = (pseudo wall's gradient)x.
So, where the pseudo wall's gradient was 1 (or at 45 degrees), then the xSpeed would become the ySpeed for the ball in question (and vice versa).
So you replaced the x value with the y value. (So if the walls gradient was 2, and the balls path's gradient was 1, the reflected balls gradient would be worked out by (2x)=1(y/2) => y = 2*2x = > y = 4x, so the gradient of the reflected balls path should be 4)

This is what I got for my code using this idea (I run it twice, swapping which one is circle1, and circle2):
PHP Code:
``` function twoCircleCollision(circle1,circle2,circle1xSpd,circle1ySpd,circle2xSpd,circle2ySpd):Number {     mag = circle2xSpd*circle2xSpd + circle2ySpd*circle2ySpd /*since they swap momentums,  and I am currently not concerned with mass, I used the magnitude of circle2 and apply it to circle1.  Note: I know it's actually the magnitude squared, but if I square rooted it now I would just have to square it again  anyway later, so I kept it as the squared value*/     grad1 = -( (circle1.x-circle2.x) / (circle1.y-circle2.y) ) /*gradient of the line between  them multiplied by -1 and inverted to give the gradient of the perpendicular line I call the pseudo wall*/     grad2 = (circle1ySpd-circle2ySpd) / (circle1xSpd-circle2xSpd) /*circle1's relative  velocities to circle2 are used to make grad2, the gradient of the original path of circle1*/     coE = (grad1*grad1) / grad2 //the coefficient of x for the new path of circle1     NewXspeed = Math.sqrt(  mag / (1+coE*coE)  ) /*Outside the function I also record  coE, and to get the Yspeed for the circle, I multiply the NewXspeed by coE*/     return NewXspeed }  ```
I know. I'm crazy.
I think I'm missing a negative and an inverse in there, or vice versa, 'cause usually using that code they will bounce off at right angles to the direction I would have expected...

Yeah... if you can follow that you can tell me if I've gone completely the other way... (btw, I suggest using paper and an example to check it. I used pseudo walls: x = -2, y = 1. And the circle1's: x = -1, y = 2)

EDIT: At this point I would like to point out that if anyone else has any comments on my lack of understanding, I'm sure that newblack wouldn't mind you adding it (Right?). Who knows, it might even help me.

EDIT2: I just want to let you know that the code you gave me is working 100% with the proper momentum. Now I just want to know WHY.  Reply With Quote

18. Thank you newblack, you helped me so much. I didn't realise that it was so simple; dx was the proportion of the xvelocity that takes part in the collision! My problem was that I over complicated the code you gave me. I kept trying to link it with the website that I posted earlier; until today. I realised that it had nothing to do with projections and dot products...
I'm sure it was obvious to you, and that's why you didn't explain it... but it confused me a lot. It's funny, last year I tried graphing the different resultant xspeeds based on the angle (given an input of 1). I couldn't think how to combine that with pythagoras. But this is how you do it.

Just 1 thing that bugs me still... what happened to the masses in the momentum part of the impulse equation?

EDIT: Oh yes. Please ignore my previous post as ramblings of a madman.

EDIT2: I think you told me equation incorrectly to begin with; according to this website, you only use the mass(es) once.
http://www.euclideanspace.com/physic...ulse/index.htm  Reply With Quote

19. Here is a good billards game tutorial:
http://www.bezzmedia.com/swfspot/tut...h8/8_Ball_Pool  Reply With Quote

20. Nice try, Eager Beaver, but;
-That ones collision detection is no better than a simple: "If the distance between them will be the averaged(?) radius next frame, then they have collided".
-They also, oddly, have also decided that: "at exactly halfway between the frames they have collided"
-The collision calculation is actually exactly the same as the one newblack gave me, except that it doesn't take into account masses. (They just named the variables differently)

Also, @newblack; I see the dot product now! Aren't you proud?
I've also figured out the calculations for an exact collision from circle to a rectangle (I think)!  Reply With Quote

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•

 » Home » Movies » Tutorials » Submissions » Board » Links » Reviews » Feedback » Gallery » Fonts » The Lounge » Sound Loops » Sound FX » About FK » Sitemap 