Question

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 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));

}
Was it helpful?

Solution

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.

OTHER TIPS

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

In this line:

CGPoint impulse = ccpMult(mtd, i);

mtd needs to have been normalised. The error happened because in the original code mtd was normalized in a previous line but not in your code. You can fix it by doing something like this:

CGPoint impulse = ccpMult(ccpNormalize(mtd), i);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow