[solved] 2d circle to circle collision response

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
User avatar
zeid
Chaos Rift Junior
Chaos Rift Junior
Posts: 201
Joined: Fri Apr 24, 2009 11:58 pm

[solved] 2d circle to circle collision response

Post 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;
         }
      }
Last edited by zeid on Tue Apr 20, 2010 1:19 pm, edited 1 time in total.
Axolotl Pop!
Image
Or, try it for free.

For many more free games online visit www.sam-zeid.com
qpHalcy0n
Respected Programmer
Respected Programmer
Posts: 387
Joined: Fri Dec 19, 2008 3:33 pm
Location: Dallas
Contact:

Re: 2d circle to circle collision response

Post 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 //
}
User avatar
MrDeathNote
ES Beta Backer
ES Beta Backer
Posts: 594
Joined: Sun Oct 11, 2009 9:57 am
Current Project: cocos2d-x project
Favorite Gaming Platforms: SNES, Sega Megadrive, XBox 360
Programming Language of Choice: C/++
Location: Belfast, Ireland
Contact:

Re: 2d circle to circle collision response

Post 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.
http://www.youtube.com/user/MrDeathNote1988

Image
Image

"C makes it easy to shoot yourself in the foot. C++ makes it
harder, but when you do, it blows away your whole leg." - Bjarne Stroustrup
User avatar
Bakkon
Chaos Rift Junior
Chaos Rift Junior
Posts: 384
Joined: Wed May 20, 2009 2:38 pm
Programming Language of Choice: C++
Location: Indiana

Re: 2d circle to circle collision response

Post by Bakkon »

When working on a Peggle clone, I found this PDF and it was extremely useful.

http://www.megaupload.com/?d=GGY4AH27
User avatar
xiphirx
Chaos Rift Junior
Chaos Rift Junior
Posts: 324
Joined: Mon Mar 22, 2010 3:15 pm
Current Project: ******** (Unkown for the time being)
Favorite Gaming Platforms: PC
Programming Language of Choice: C++
Contact:

Re: 2d circle to circle collision response

Post 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
StarCraft II Zerg Strategy, open to all levels of players!

Looking for paid work :< Contact me if you are interested in creating a website, need a web design, or anything else you think I'm capable of :)
Post Reply