Page 1 of 1

[solved] 2d circle to circle collision response

Posted: Wed Apr 14, 2010 7:45 am
by zeid
So I'm trying to set up a simple 2d physics system at the moment and for some reason I can't seem to get my circle to circle collision responses working correctly. I'm sure it's a typo or similar but it's not getting anywhere so I felt I should probably post it up online and see if I can get a hand sussing out the problem.
NOTE: the collision detection works, I am having trouble with the response.

Code: Select all

 void circleToCircleCollision(Entity* circleA, Entity* circleB)
   {
      //Determines the strength of reflection, this will be expanded on later.
      float bounce=1.0f;
      //Note: i store the deltaX and deltaY in a vector for convenience.
      vector2<float> delta(circleA->x-circleB->x,circleA->y-circleB->y);

      float  distance=delta.magnitude();

      //entities have a width and a height, from this we can create a radius by using the one that is larger (this is a temporary way of getting the combined radius of each entity)
      float totalRadius=0.0f;
      circleA->sizeX>circleA->sizeY?totalRadius+=circleA->sizeX:totalRadius+=circleA->sizeY;
      circleB->sizeX>circleB->sizeY?totalRadius+=circleB->sizeX:totalRadius+=circleB->sizeY;

      //simple circle to circle collision detection
      if(distance <= totalRadius)
      {
         delta.normalise();

         float  velocityA = (circleA->motion.x*delta.x) + (circleA->motion.y*delta.y);
         float  velocityB = (circleB->motion.x*delta.x) + (circleB->motion.y*delta.y);
         
         if((velocityA-velocityB)!=0.0f)
         {
            float  deltatime = (totalRadius - distance)/(velocityA - velocityB);
            //Move the circles so they are in the position where they had just touched.
            circleA->x-=circleA->motion.x*deltatime;
            circleA->y-=circleA->motion.y*deltatime;
            
            circleB->x-=circleB->motion.x* deltatime;
            circleB->y-=circleB->motion.y* deltatime;
            
            //
            float  projectionA=(circleA->motion.x*delta.x) + (circleA->motion.y*delta.y);
            float  _projectionA=(circleA->motion.y*delta.x) - (circleA->motion.x*delta.y);
            
            float  projectionB=(circleB->motion.x*delta.x) + (circleB->motion.y*delta.y);
            float  _projectionB=(circleB->motion.y*delta.x) - (circleB->motion.x*delta.y);
            
            //new velocity
            float vA=(projectionA+bounce*(projectionB - projectionA))/(1.0f+(circleA->mass/circleB->mass));
            
            float vB=(projectionB+bounce*(projectionA - projectionB))/(1.0f+(circleA->mass/circleB->mass));
            
            //Use the attained data to assign the new motion vector
            circleA->motion.x =((vA*delta.x) - (_projectionA*delta.y));
            circleA->motion.y =((vA*delta.y) + (_projectionA*delta.x));
            
            circleB->motion.x =((vB*delta.x) - (_projectionB*delta.y));
            circleB->motion.y =((vB*delta.y) + (_projectionB*delta.x));
            
            //The balls position was shifted 'back in time' so we shift it once more to account for this.
            circleA->x+=circleA->motion.x* deltatime;
            circleA->y+=circleA->motion.y* deltatime;
            
            circleB->x+=circleB->motion.x* deltatime;
            circleB->y+=circleB->motion.y* deltatime;
         }
      }

Re: 2d circle to circle collision response

Posted: Wed Apr 14, 2010 11:08 am
by qpHalcy0n
With this sort of thing, it may possibly be easier to deal with the reaction w/ a vector based approach.

So the collision works, the easiest way to deal with the reaction is a reflection about the impact normal. If you take the vector from one circle's center to the other's this is basically your impact normal...draw it out and you'll see that clearly (normalize this). For one circle you'll want the normal to face one direction, for the other circle you'd want to flip it to face the other way (opposite reactions).

If you take the horizontal and vertical components of each ball's velocity and pack them into a 2 component vector, the resultant velocity will be just a reflection about the impact normal. Reflections preserve length, so you would not need to normalize the direction vector there....which is very nice because you can add in a scalar that "dumbs down" the impact....making it more or less elastic. If you just use the resultant velocity directly then you have a perfect collision with no loss of momentum. Depending on what you're doing you may want to add a dampening factor to that result. Also if you're figuring mass into the equation, you can just transfer energy from one to the other in that final result as a large or small damping factor...which is just a vector scaling. Eg: transferring energy from a large mass to a small one would result in a larger resultant vector, taking energy away would dampen the reaction giving a smaller resultant vector.

Given an incoming vector and the impact normal (normalized) you get an equation:

Code: Select all

vec2 reflect_vector( vec2 incoming, vec2 normal )
{
       return incoming - 2.0 * normal * dot_product( normal, incoming );
}

so the pseudo wraps up like:

Code: Select all

if( collision )
{  
       float dampingFactor = 1.0F;

       vec2 collisionNormal = vec2( circleB.Center - circleA.center );
       vec2 circleAVel = circleA.GetVelocity();
       vec2 circleBVel = circleB.GetVelocity();

       // Obtain resultant vector for Circle A //
       circleAVel = reflect_vector( circleAVel, collisionNormal ) * dampingFactor;
    
       // Flip the normal //
       collisionNormal *= -1.0F;

      // Obtain resultant vector fir Circle B //
      circleBVel = reflect_vector( circleBVel, collisionNormal ) * dampingFactor;

      // Update positions & shit //
}

Re: 2d circle to circle collision response

Posted: Wed Apr 14, 2010 12:31 pm
by MrDeathNote
This is the code i use for my air hockey game:

Code: Select all

bool collide(Circle& a, Circle& b)
{
    // separation vector
    Vector d(b.pos - a.pos);

    // distance between circle centres, squared
    float distance_squared = d.DotProduct(d);

    // combined radius squared
    float radius = b.rad + a.rad;
    float radius_squared = radius * radius;
 
    // circles too far apart
    if(distance_squared > radius_squared) 
        return false;

    // distance between circle centres
    float distance = (float) sqrt(distance_squared);

    // normal of collision
    Vector ncoll = (d / distance);

    // penetration distance
    float dcoll = (radius - d);

    // inverse masses (0 means, infinite mass, object is static).
    float ima = (a.mass > 0.0f)? 1.0f / a.mass : 0.0f;
    float imb = (b.mass > 0.0f)? 1.0f / b.mass : 0.0f;

    // separation vector
    Vector separation_vector = ncoll * (dcoll / (ima + imb));

    // separate the circles
    a.pos -= separation_vector * ima;
    b.pos += separation_vector * imb;

    // combines velocity
    Vector vcoll = (b.vel - a.vel);
   
    // impact speed 
    vector vn = vcoll.DotProduct(ncoll);
    
    // obejcts are moving away. dont reflect velocity
    if(vn > 0.0f) 
        return true; // we did collide

    // coefficient of restitution in range [0, 1].
    const float cor = 0.95f; // air hockey -> high cor

    // collision impulse
    float j = -(1.0f + cor) * (vn) / (ima + imb);

    // collision impusle vector
    Vector impulse = j * ncoll;

    // change momentum of the circles
    a.vel -= impulse * ima;
    b.vel += impulse * imb;

    // collision reported
    return true;
}

Circle needs mass, velocity, radius and position.

Re: 2d circle to circle collision response

Posted: Wed Apr 14, 2010 5:17 pm
by Bakkon
When working on a Peggle clone, I found this PDF and it was extremely useful.

http://www.megaupload.com/?d=GGY4AH27

Re: 2d circle to circle collision response

Posted: Wed Apr 14, 2010 8:10 pm
by xiphirx
I found this post a few months back, and bookmarked it for future use,
http://n.domaindlx.com/nataku92/java/tu ... ction.html