views:

1260

answers:

2

I implemented the code from the question "Ball to Ball Collision - Detection and Handling" in Objective-C. However, whenever the balls collide at an angle their velocity increases dramatically. All of the vector math is done using cocos2d-iphone, with the header CGPointExtension.h. What is the cause of this undesired acceleration?

The following is an example of this increase in speed:

Input:
mass == 12.56637
velocity.x == 1.73199439
velocity.y == -10.5695238

ball.mass == 12.56637
ball.velocity.x == 6.04341078
ball.velocity.y == 14.2686739

Output:
mass == 12.56637
velocity.x == 110.004326
velocity.y == -10.5695238

ball.mass == 12.56637
ball.velocity.x == -102.22892
ball.velocity.y == -72.4030228

#import "CGPointExtension.h"
#define RESTITUTION_CONSTANT (0.75) //elasticity of the system

- (void) resolveCollision:(Ball*) ball
{
    // get the mtd (minimum translation distance)
    CGPoint delta = ccpSub(position, ball.position);
    float d = ccpLength(delta);
    // minimum translation distance to push balls apart after intersecting
    CGPoint mtd = ccpMult(delta, (((radius + ball.radius)-d)/d)); 


    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / [self mass]; 
    float im2 = 1 / [ball mass];

    // push-pull them apart based off their mass
    position = ccpAdd(position, ccpMult(mtd, (im1 / (im1 + im2))));
    ball.position = ccpSub(ball.position, ccpMult(mtd, (im2 / (im1 + im2))));

    // impact speed
    CGPoint v = ccpSub(velocity, ball.velocity);
    float vn = ccpDot(v,ccpNormalize(mtd));

    // sphere intersecting but moving away from each other already
    if (vn > 0.0f) return;

    // collision impulse
    float i = (-(1.0f + RESTITUTION_CONSTANT) * vn) / ([self mass] + [ball mass]);
    CGPoint impulse = ccpMult(mtd, i);


    // change in momentum
    velocity = ccpAdd(velocity, ccpMult(impulse, im1));
    ball.velocity = ccpSub(ball.velocity, ccpMult(impulse, im2));

}
+5  A: 

Having reviewed the original code and the comments by the original poster, the code seems the same, so if the original is a correct implementation, I would suspect a bad vector library or some kind of uninitialized variable.

Why are you adding 1.0 to the coefficient of restitution?

From: http://en.wikipedia.org/wiki/Coefficient_of_restitution

The COR is generally a number in the range [0,1]. Qualitatively, 1 represents a perfectly elastic collision, while 0 represents a perfectly inelastic collision. A COR greater than one is theoretically possible, representing a collision that generates kinetic energy, such as land mines being thrown together and exploding.

Another problem is this:

/ (im1 + im2)

You're dividing by the sum of the reciprocals of the masses to get the impulse along the vector of contact - you probably should be dividing by the sum of the masses themselves. This is magnifying your impulse ("that's what she said").

Cade Roux
nice catch 1.0 -> 1.0f
curtisk
The problem is the same with 1.0f
doomdayx
What is the difference between 1.0 and 1.0f anyway?
doomdayx
My question is why is your effective coefficient of restitution 1.75 - i.e. you are adding energy into the system at time of collision which would obviously be converted into kinetic energy in the balls.
Cade Roux
The difference between 1.0f and 1.0 is that you are specifying 1.0f to be 1.0 of data type float (as opposed to whatever the compiler defaults to - presumably its options are either float or double). I don't think that's material to the problem.
Cade Roux
According to most style guidelines, instead of #define, you should use a const float.
Cade Roux
I removed the addition of 1.0 to the COR and the acceleration remains. The difference between 1.0 and 1.75 would not increase the velocity from 1.73 pixels/refresh to 110 pixels/refresh as it does above. Some other aspect is causing this effect.
doomdayx
See the update to my answer regardng the impulse scaling
Cade Roux
+3  A: 

I'm the one who wrote the original ball bounce code you referenced. If you download and try out that code, you can see it works fine.

The following code is correct (the way you originally had it):

// collision impulse
float i = (-(1.0f + RESTITUTION_CONSTANT) * vn) / (im1 + im2);
CGPoint impulse = ccpMult(mtd, i);

This is very common physics code and you can see it nearly exactly implemented like this in the following examples:

This is correct, and it ~isn't~ creating a CoR over 1.0 like others have suggested. This is calculating the relative impulse vector based off mass and Coefficient of Restitution.

Ignoring friction, a simple 1d example is as follows:

J = -Vr(1+e) / {1/m1 + 1/m2}

Where e is your CoR, Vr is your normalized velocity and J is a scalar value of the impulse velocity.

If you plan on doing anything more advanced than this I suggest you use one of the many physics libraries already out there. When I used the code above it was fine for a few balls but when I ramped it up to several hundred it started to choke. I've used the Box2D physics engine and its solver could handle more balls and it is much more accurate.

Anyway, I looked over your code and at first glance it looks fine (it is a pretty faithful translation). It is probably a small and subtle error of a wrong value being passed in, or a vector math problem.

I don't know anything concerning iPhone development but I would suggest setting a breakpoint at the top of that method and monitoring each steps resulting value and finding where the blow-up is. Ensure that the MTD is calculated correctly, the impact velocities, etc, etc until you see where the large increase is getting introduced.

Report back with the values of each step in that method and we'll see what we have.

Simucal