Mass Ball-to-Ball Collision Handling (as in, lots

2019-02-25 01:19发布

问题:

Update: Found out that I was using the radius as the diameter, which was why the mtd was overcompensating.

Another Update: The reason my balls are overlapping seem to be because there's only one check for every collision. After some research, some are saying that one of the ways you could prevent overlapping with objects piling up is to recursively perform the collision check. This works to an extent, and I suspect that it'd work even better if the physics were a lot more accurate. I'll update again if I find any more solutions. Also, I'm using Simucal's collision correction code.

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;

回答1:

Your "resulting code" looks correct. Do you have a simple test case that shows the overcorrection (e.g. x1=0, x1=3, y1=y2=0, r1=r2=2)?

Also, do you get good results if you neglect the overlap and keep everything else, as if the balls were soft rubber?

EDIT:

This part is wrong:

PVector velocity = new PVector(vx,vy);  
...
collider.vy += (bcf - bci) * collision.y;

When two balls get close they damp each other's velocity until they both stop. But on the bright side, you've invented solids (just kidding). Turn off the overlap stuff and don't worry about it until you have this part working.

Getting this part working will be just about impossible without an understanding of basic physics. Do you need a hand?