Object Collision

Member
Posts: 196
Joined: 2002.04
Post: #1
Hi,
The code below tests collision between two balls. The problem I'm having is that if the balls don't collide exactly head on they bounce away in an odd manner. How do I fix this?


Code:
x1 += xVect1;
y1 += yVect1;

x2 += xVect2;
y2 += yVect2;

radius1 = 5.0;
radius2 = 5.0;

//This checks the distance
distance = distance(x1, y1, x2, y2);

if (distance <= (radius1 + radius2)) {
xVect = -xVect;
yVect = -yVect;
}

Thanks,
Iceman
Quote this message in a reply
w_reade
Unregistered
 
Post: #2
I'm going to be assuming elastic collisions and equal masses, by the way; I'm not in the mood for a more involved discussion. Anyway.

The main problem is that touching objects push one another away, and you can't work out where "away" is just by knowing the velocities and the fact that a collision has occurred - you need to find out more about the collision. In this case, "away" means "along the line perpendicular to the surfaces where they touch (in the appropriate direction)".

Since you're dealing with balls, this isn't too hard; the line through any normal of a sphere (or circle) always goes through the centre of the sphere, so your "away" vector from ball2 to ball1 is just [ball1.position - ball2.position]. Normalise this vector and remember it; I'll be calling it N from now on. We now know the line the forces are acting along.

We also want to consider the relative velocity of the balls - how hard they actually hit one another. Let V (approach velocity) be [ball2.velocity - ball1.velocity]. From ball1's point of view, this is how ball2 appears to be moving.

Now calculate the approach speed along the line of force (which we'll call s); happily, that's just DotProduct(N, V). Since I've made the helpful assumptions at the top, I know that (along the line of force) the balls will be travelling apart at the same speed that they were approaching one another at, and that they'll both get the same change in velocity (although in opposite directions).

Multiply N by s to get W - the approach velocity along the line of force from ball2 to ball1. Now, thanks to those assumptions, it's positively trivial; add W to ball1.velocity, and subtract W from ball2.velocity. Done!

In plain language, it goes something like this (assuming suitable arithmetic operators for the Vector class):

Code:
void CollideBalls (Ball *ball1, Ball *ball2)
{
    Vector normal = Normalise(ball1.position - ball2.position);
    Vector approachVel = ball2.velocity - ball1.velocity;
    float projectedApproachSpeed = DotProduct(normal, approachVel);
    Vector velocityChange = normal * projectedApproachSpeed;
    
    ball1->velocity += velocityChange;
    ball2->velocity -= velocityChange;
}

Differing masses and coefficients of restitution cause a bit more complication (get a physics book); the fact that you need to time-step the whole thing causes a fair bit more. This should be enough to get you started though.

Anyway, I'm a lot more tired than I thought I was... if I've said/done something stupid, I'm sure someone'll pick me up on it.
Quote this message in a reply
Moderator
Posts: 916
Joined: 2002.10
Post: #3
I am now hosting a nice cocoa project for people to look at called Falling Balls. It shows circle based collision and bouncing.

App:
http://www.hkasoftware.com/skyhawk/FallingBalls.app.sit

source:
http://www.hkasoftware.com/skyhawk/falli...source.sit
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #4
Quote:Originally posted by w_reade
I'm going to be assuming elastic collisions and equal masses

Yes, sorry I meant that too. This stuff sounds pretty cool and complex I need to check out some physics books Grin. What do they call this kind of collision? I was having trouble finding collision examples in some of the physics books I looked at already.

Also I meant to write this instead but I forgot:

Code:
if (distance <= (radius1 + radius2)) {
xVect2 = -xVect2;
yVect2 = -yVect2;

xVect1 = -xVect1;
yVect1 = -yVect1;
}

Thanks so much,
Iceman
Quote this message in a reply
w_reade
Unregistered
 
Post: #5
I'll have another go; it's not actually as complex as it looks. I'm assuming everything I was before, including the previously-unstated assumption that the balls have actually hit one another Wink. Oh, and that they aren't spinning, and never will.

If you multiply the balls' velocities by -1, all you're doing is reversing their directions of movement - they're still travelling along the same lines that they were before the collision. This will only work right if they hit one another head-on at the same speed (ie with opposite velocities).

If you think about a glancing collision, it should be clear that (most of the time) neither ball will be moving along the same line after the collision as it was before. Therefore, just reversing velocities won't be very helpful to you; things need to be a little bit more involved.

By Newton's 2nd law, the force ball1 feels from the collision is equal and opposite to the force ball2 feels. Since they're the same mass but feeling equal and opposite forces, the two balls will experience equal and opposite changes in velocity (by Newton's 3rd law, F = ma). That is, ball1VelChange == -ball2VelChange, hence the final lines:

Code:
ball1->velocity += velocityChange;
    ball2->velocity -= velocityChange;

Of course, you need to know what velocityChange actually is. I'll try and walk through this again...

The direction in which each ball exerts force upon the other is perpendicular to the surfaces where they touch, and this direction is parallel to the line which connects the balls' centres at the moment of collision. The velocityChange vector will also be in this direction, and I can get a vector in this direction by subtracting one ball's position vector from the other's.

I don't just want any vector on this line, though; I want a vector of magnitude 1, which characterises the direction without any excess baggage, so I normalise the vector:

Code:
Vector normal = Normalise(ball1.position - ball2.position);

...so, normal is a unit vector pointing from ball2 towards ball1, and I know that for some x, velocityChange = x * normal.

Ok, how do I determine x? Right.

Because I know a bit of physics, I know that:

(1) I can use whatever reference frame I please; as long as the relative velocities of the balls are preserved, the velocityChange vector will come out the same regardless.

(2) since the forces only act along the normal vector, there won't be any change to either ball's velocity in the direction perpendicular to normal, so I can effectively discard the perpendicular velocity components and treat it as a one-dimensional collision. Remember, no spinning.

(3) in an elastic collision between two objects of equal mass, recessionSpeedAfterCollision == approachSpeedBeforeCollision.

Now... by (1), I choose the reference frame in which ball1 appears to be at rest (for simplicity's sake), and calculate the apparent velocity of ball2:

Code:
Vector approachVel = ball2.velocity - ball1.velocity;

By (2), I can discard the tangential (perpendicular to normal) component of approachVel. To do this, I get the dot product of normal and approachVel, which returns a scalar equal to the component of approachVel in the direction of normal (ie we project approachVel onto the line of normal):

Code:
float projectedApproachSpeed = DotProduct(normal, approachVel);

Now, staying in the same reference frame (1) and ignoring the tangential components (2), we've reduced the problem to that of a moving ball solidly (ie not-glancingly) hitting a static ball. We can see that (3) is satisfied by letting x = projectedApproachSpeed, so:

Code:
Vector velocityChange = normal * projectedApproachSpeed;

and... Bingo! You have your velocityChange, and can change the balls' velocities as above.

Now, I have a feeling that I haven't been all that clear this time round either. If you don't get what I'm on about, please ask for clarification on specific points; my trouble is that I don't really know how much you know, so I can't tell whether (to you) I'm spouting meaningless jargon, being horrifyingly patronising, or something in between.

Still, I've typed all this out and I'm damn well going to post it after all that effort Wink. Hope it helps.
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #6
Ok I think I got it. It's just that getting all the pieces together that you explain above are confusing me now. Might I add the code above is a damn good example everything else I've looked at on the internet has been totally abstract with very little explanation. And "previously-unstated assumption that the balls have actually hit one another. Oh, and that they aren't spinning, and never will." Yes it's perfect elastic, no rotation, no spinning physics. So here's the code I've gotten so far:

Code:
Normal (float x, float y, float normal[])
{

t = sqrt(x * x + y * y);

normal[0] = x / t;
normal[1] = y / t;
}

DotProd (x1, y1 , x2, y2)
{
return (x1 * x2 + y1 * y2);
}

void collision (void)
{
x1 += xVect1;
y1 += yVect1;

x2 += xVect2;
y2 += yVect2;

xVelocity = xVect2 - xVect1;
yVelocity = yVect2 - yVect1;

// Normalize
Normal (x1 - x2, y1 - y2, normal);

approach = DotProd(normal[0], normal[1], xVelocity, yVelocity);

if (distance <= radius1 + radius2) {
xNewVect = nx * approach;
yNewVect = ny * approach;
}

Ok I know I'm soooo close but still struggling. How do I get the NewVect1 and NewVect2?

Thanks,
Iceman
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #7
Ok I didn't get the code working but the formula below works fine for now.
Code:
xNewVect1 = xOldVect2;
xNewVect1 = xOldVect2;

xNewVect2 = xOldVect1;
xNewVect2 = xOldVect1;
This has really helped me out a lot with normals. I didn't know you could also use them to make walls and stuff. So cool! Although, I still have a lot to learn.

Thanks so much,
Iceman
Quote this message in a reply
w_reade
Unregistered
 
Post: #8
Um, oops... I meant to reply to your last post, but I've been employed lately and... ick. Anyway, yeah, that's a nice old-skool collision algorithm and perfectly adequate for most purposes.

Going back to your post-before-last... I'm not totally sure about the code you posted... did you copy and paste it? If so, there are a couple of things... firstly, in terms of calculation:

Code:
xNewVect = nx * approach;
yNewVect = ny * approach;

should surely be

Code:
xNewVect = normal[0] * approach;
yNewVect = normal[1] * approach;

and then you could just set

Code:
xVect1 += xNewVect;
yVect1 += yNewVect;

xVect2 -= xNewVect;
yVect2 -= yNewVect;

to get the required bouncing. The other thing is that it'd be easier on the machine if you find out whether the balls have hit one another before worrying about what to do if they have - ie

Code:
void collision (void)
{
// ball movement (Yes?)

x1 += xVect1;
y1 += yVect1;

x2 += xVect2;
y2 += yVect2;

// test for collision

if (distance > radius1 + radius2)
{
return;
}

// if we get here, the balls have collided,
// so we do the collision calculations

xVelocity = xVect2 - xVect1;
yVelocity = yVect2 - yVect1;

// Normalize
Normal (x1 - x2, y1 - y2, normal);

approach = DotProd(normal[0], normal[1], xVelocity, yVelocity);

// calculate velocity change

xNewVect = normal[0] * approach;
yNewVect = normal[1] * approach;

// change velocities

xVect1 += xNewVect;
yVect1 += yNewVect;

xVect2 -= xNewVect;
yVect2 -= yNewVect;
}

I think that's right, but I'm not entirely sure what language we're using. One of the Basics? I started out thinking it was C, but if so the copious numbers of global variables are deeply unsettling to me... what language am I actually trying to speak in? Blush

Anyway, give it a try and let me know if it works...
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #9
Yes it's C. "global variables are deeply unsettling to me", sorry I was trying to write it quickly since I'm typing this on a PC, thus I can't copy code from my Mac.

Thanks,
Iceman
Quote this message in a reply
w_reade
Unregistered
 
Post: #10
"deeply unsettling"... well, you know Wink. Not intended as an attack, I did think what you posted might not have been the actual code you were using.

If you are still interested in sorting out a "correct" collision and it doesn't quite work, you should probably post your actual code and I'd be happy to help out.
Quote this message in a reply
Member
Posts: 196
Joined: 2002.04
Post: #11
Thank you so much for all your help. This really got me thinking about how to get collision physics working. So far it looks really cool; although, I'm still working on some bugs I can get worked out on my own.

Thanks hope to show everyone my new game soon,
Iceman

P.S. I'm getting Juno or something cheap for my Mac soon. Smile
Quote this message in a reply
Post Reply