Mass Ball-to-Ball Collision Handling (as in, lots of balls)
- by BlueThen
Update: Found out that I was using the radius as the diameter, which was why the mtd was overcompensating.
Hi, StackOverflow. I've written a Processing program awhile back simulating ball physics. Basically, I have a large number of balls (1000), with gravity turned on. Detection works great, but my issue is that they start acting weird when they're bouncing against other balls in all directions.
I'm pretty confident this involves the handling. For the most part, I'm using Jay Conrod's code. One part that's different is
if (distance > 1.0)
return;
which I've changed to
if (distance < 1.0)
return;
because the collision wasn't even being performed with the first bit of code, I'm guessing that's a typo.
The balls overlap when I use his code, which isn't what I was looking for. My attempt to fix it was to move the balls to the edge of each other:
float angle = atan2(y - collider.y, x - collider.x);
float distance = dist(x,y, balls[ID2].x,balls[ID2].y);
x = collider.x + radius * cos(angle);
y = collider.y + radius * sin(angle);
This isn't correct, I'm pretty sure of that.
I tried the correction algorithm in the previous ball-to-ball topic:
// get the mtd
Vector2d delta = (position.subtract(ball.position));
float d = delta.getLength();
// minimum translation distance to push balls apart after intersecting
Vector2d mtd = delta.multiply(((getRadius() + ball.getRadius())-d)/d);
// resolve intersection --
// inverse mass quantities
float im1 = 1 / getMass();
float im2 = 1 / ball.getMass();
// push-pull them apart based off their mass
position = position.add(mtd.multiply(im1 / (im1 + im2)));
ball.position = ball.position.subtract(mtd.multiply(im2 / (im1 + im2)));
except my version doesn't use vectors, and every ball's weight is 1. The resulting code I get is this:
PVector delta = new PVector(collider.x - x, collider.y - y);
float d = delta.mag();
PVector mtd = new PVector(delta.x * ((radius + collider.radius - d) / d), delta.y * ((radius + collider.radius - d) / d));
// push-pull apart based on mass
x -= mtd.x * 0.5;
y -= mtd.y * 0.5;
collider.x += mtd.x * 0.5;
collider.y += mtd.y * 0.5;
This code seems to over-correct collisions. Which doesn't make sense to me because in no other way do I modify the x and y values of each ball, other than this.
Some other part of my code could be wrong, but I don't know. Here's the snippet of the entire ball-to-ball collision handling I'm using:
if (alreadyCollided.contains(new Integer(ID2))) // if the ball has already collided with this, then we don't need to reperform the collision algorithm
return;
Ball collider = (Ball) objects.get(ID2);
PVector collision = new PVector(x - collider.x, y - collider.y);
float distance = collision.mag();
if (distance == 0) {
collision = new PVector(1,0);
distance = 1;
}
if (distance < 1)
return;
PVector velocity = new PVector(vx,vy);
PVector velocity2 = new PVector(collider.vx, collider.vy);
collision.div(distance); // normalize the distance
float aci = velocity.dot(collision);
float bci = velocity2.dot(collision);
float acf = bci;
float bcf = aci;
vx += (acf - aci) * collision.x;
vy += (acf - aci) * collision.y;
collider.vx += (bcf - bci) * collision.x;
collider.vy += (bcf - bci) * collision.y;
alreadyCollided.add(new Integer(ID2));
collider.alreadyCollided.add(new Integer(ID));
PVector delta = new PVector(collider.x - x, collider.y - y);
float d = delta.mag();
PVector mtd = new PVector(delta.x * ((radius + collider.radius - d) / d), delta.y * ((radius + collider.radius - d) / d));
// push-pull apart based on mass
x -= mtd.x * 0.2;
y -= mtd.y * 0.2;
collider.x += mtd.x * 0.2;
collider.y += mtd.y * 0.2;
Thanks. (Apologies for lack of sources, stackoverflow thinks I'm a spammer)