Ball to Ball Collision

With the help of the Stack Overflow community I've written a pretty basic-but fun physics simulator.

替代文字

You click and drag the mouse to launch a ball. It will bounce around and eventually stop on the "floor".

My next big feature I want to add in is ball to ball collision. The ball's movement is broken up into ax and y speed vector. I have gravity (small reduction of the y vector each step), I have friction (small reduction of both vectors each collision with a wall). The balls honestly move around in a surprisingly realistic way.

I guess my question has two parts:

  • What is the best method to detect ball to ball collision?
    Do I just have an O(n^2) loop that iterates over each ball and checks every other ball to see if it's radius overlaps?
  • What equations do I use to handle the ball to ball collisions? Physics 101
    How does it effect the two balls speed x/y vectors? What is the resulting direction the two balls head off in? How do I apply this to each ball?
  • 替代文字

    Handling the collision detection of the "walls" and the resulting vector changes were easy but I see more complications with ball-ball collisions. With walls I simply had to take the negative of the appropriate x or y vector and off it would go in the correct direction. With balls I don't think it is that way.

    Some quick clarifications: for simplicity I'm ok with a perfectly elastic collision for now, also all my balls have the same mass right now, but I might change that in the future.


    Edit: Resources I have found useful

    2d Ball physics with vectors: 2-Dimensional Collisions Without Trigonometry.pdf
    2d Ball collision detection example: Adding Collision Detection


    Success!

    I have the ball collision detection and response working great!

    Relevant code:

    Collision Detection:

    for (int i = 0; i < ballCount; i++)  
    {  
        for (int j = i + 1; j < ballCount; j++)  
        {  
            if (balls[i].colliding(balls[j]))  
            {
                balls[i].resolveCollision(balls[j]);
            }
        }
    }
    

    This will check for collisions between every ball but skip redundant checks (if you have to check if ball 1 collides with ball 2 then you don't need to check if ball 2 collides with ball 1. Also, it skips checking for collisions with itself).

    Then, in my ball class I have my colliding() and resolveCollision() methods:

    public boolean colliding(Ball ball)
    {
        float xd = position.getX() - ball.position.getX();
        float yd = position.getY() - ball.position.getY();
    
        float sumRadius = getRadius() + ball.getRadius();
        float sqrRadius = sumRadius * sumRadius;
    
        float distSqr = (xd * xd) + (yd * yd);
    
        if (distSqr <= sqrRadius)
        {
            return true;
        }
    
        return false;
    }
    
    public void resolveCollision(Ball ball)
    {
        // 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)));
    
        // impact speed
        Vector2d v = (this.velocity.subtract(ball.velocity));
        float vn = v.dot(mtd.normalize());
    
        // sphere intersecting but moving away from each other already
        if (vn > 0.0f) return;
    
        // collision impulse
        float i = (-(1.0f + Constants.restitution) * vn) / (im1 + im2);
        Vector2d impulse = mtd.multiply(i);
    
        // change in momentum
        this.velocity = this.velocity.add(impulse.multiply(im1));
        ball.velocity = ball.velocity.subtract(impulse.multiply(im2));
    
    }
    

    Source Code: Complete source for ball to ball collider.

    If anyone has some suggestions for how to improve this basic physics simulator let me know! One thing I have yet to add is angular momentum so the balls will roll more realistically. Any other suggestions? Leave a comment!


    To detect whether two balls collide, just check whether the distance between their centers is less than two times the radius. To do a perfectly elastic collision between the balls, you only need to worry about the component of the velocity that is in the direction of the collision. The other component (tangent to the collision) will stay the same for both balls. You can get the collision components by creating a unit vector pointing in the direction from one ball to the other, then taking the dot product with the velocity vectors of the balls. You can then plug these components into a 1D perfectly elastic collision equation.

    Wikipedia has a pretty good summary of the whole process. For balls of any mass, the new velocities can be calculated using the equations (where v1 and v2 are the velocities after the collision, and u1, u2 are from before):

    If the balls have the same mass then the velocities are simply switched. Here's some code I wrote which does something similar:

    void Simulation::collide(Storage::Iterator a, Storage::Iterator b)
    {
        // Check whether there actually was a collision
        if (a == b)
            return;
    
        Vector collision = a.position() - b.position();
        double distance = collision.length();
        if (distance == 0.0) {              // hack to avoid div by zero
            collision = Vector(1.0, 0.0);
            distance = 1.0;
        }
        if (distance > 1.0)
            return;
    
        // Get the components of the velocity vectors which are parallel to the collision.
        // The perpendicular component remains the same for both fish
        collision = collision / distance;
        double aci = a.velocity().dot(collision);
        double bci = b.velocity().dot(collision);
    
        // Solve for the new velocities using the 1-dimensional elastic collision equations.
        // Turns out it's really simple when the masses are the same.
        double acf = bci;
        double bcf = aci;
    
        // Replace the collision velocity components with the new ones
        a.velocity() += (acf - aci) * collision;
        b.velocity() += (bcf - bci) * collision;
    }
    

    As for efficiency, Ryan Fox is right, you should consider dividing up the region into sections, then doing collision detection within each section. Keep in mind that balls can collide with other balls on the boundaries of a section, so this may make your code much more complicated. Efficiency probably won't matter until you have several hundred balls though. For bonus points, you can run each section on a different core, or split up the processing of collisions within each section.


    Well, years ago I made the program like you presented here.
    There is one hidden problem (or many, depends on point of view):

  • If the speed of the ball is too high, you can miss the collision.
  • And also, almost in 100% cases your new speeds will be wrong. Well, not speeds, but positions. You have to calculate new speeds precisely in the correct place. Otherwise you just shift balls on some small "error" amount, which is available from the previous discrete step.

    The solution is obvious: you have to split the timestep so, that first you shift to correct place, then collide, then shift for the rest of the time you have.


    You should use space partitioning to solve this problem.

    Read up on Binary Space Partitioning and Quadtrees

    链接地址: http://www.djcxy.com/p/65396.html

    上一篇: Android以编程方式为TextView设置重力

    下一篇: 球碰撞