4

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

}
Community
  • 1
  • 1
Andrew Hundt
  • 2,551
  • 2
  • 32
  • 64
  • Your physics are the issue here: you're setting up a collision that does not conserve kinetic energy, since the Coefficient of Restitution is 1.75 (over 1). Hence, this is not a programing related error. – jpinto3912 May 21 '09 at 21:03
  • Could you post the final version of your code that works? – Cade Roux May 21 '09 at 22:45
  • @jpinto3912 - don't you mean conservation of momentum? – Not Sure May 21 '09 at 23:52
  • @Cade Roux - it still is not working... using the real mass instead of the inverse simply results in the balls pushing around each other instead of bouncing off at an absurd speed. – Andrew Hundt May 22 '09 at 00:12
  • Here is the google code link to my final version of this code that I wrote. I added a couple special divide-by-zero conditions when balls are directly on top of eachother. http://code.google.com/p/simucal-projects/source/browse/ballbounce/trunk/Ball.java – mmcdole May 22 '09 at 00:24
  • I'm not sure. Why don't you check the formulas in the degenerate case. Two identical balls (mass and radius), one ball stationary, COR = 1, ball centers in line with trajectory of the second moving ball. You should see the moving ball stop dead and the second ball start moving with the velocity of the original moving ball a la Newton's cradle. – Cade Roux May 22 '09 at 00:34

3 Answers3

5

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
  • 88,164
  • 40
  • 182
  • 265
  • The problem is the same with 1.0f – Andrew Hundt May 21 '09 at 20:46
  • What is the difference between 1.0 and 1.0f anyway? – Andrew Hundt May 21 '09 at 20:47
  • 3
    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 May 21 '09 at 20:48
  • 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 May 21 '09 at 21:01
  • According to most style guidelines, instead of #define, you should use a const float. – Cade Roux May 21 '09 at 21:02
  • 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. – Andrew Hundt May 21 '09 at 21:04
3

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.

mmcdole
  • 91,488
  • 60
  • 186
  • 222
0

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);
andrewsi
  • 10,807
  • 132
  • 35
  • 51
NickT
  • 1