## Object Collision

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

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.

App:

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

source:

http://www.hkasoftware.com/skyhawk/falli...source.sit

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 . 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

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 . Hope it helps.

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

Code:

`xNewVect1 = xOldVect2;`

xNewVect1 = xOldVect2;

xNewVect2 = xOldVect1;

xNewVect2 = xOldVect1;

Thanks so much,

Iceman

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?

Anyway, give it a try and let me know if it works...

Thanks,

Iceman

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.

Thanks hope to show everyone my new game soon,

Iceman

P.S. I'm getting Juno or something cheap for my Mac soon.