Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Circle Collision Problems

This thread is locked; no one can reply to it. rss feed Print
Circle Collision Problems
dakatt
Member #10,695
February 2009

I'm having trouble getting circles to bounce off of each other. I've made 2 stand alone objects (c1 and c2, the unfilled white circles) that seem to work most of the time. My problem is when I try to make a vector to store them and to keep track of their collisions (b1 and b2 and the collision class) they act differently. You can click the lmb to grab one of the filled circles to make collisions easier.

#SelectExpand
1class CVec2D { 2public: 3 CVec2D(); 4 CVec2D(float,float); 5 ~CVec2D(){} 6 7 float GetX(){return x;} 8 float GetY(){return y;} 9 void Set(float xi,float yi){x=xi;y=yi;z=1;} 10 void SetX(float xi){x=xi;} 11 void SetY(float yi){y=yi;} 12 void Clear(){x=0.0f;y=0.0f;z=1.0f;w=1.0f;} 13 void Add(CVec2D); 14 void Add(float m){x+=m;y+=m;} 15 void Add(float xi,float yi){x+=xi;y+=yi;} 16 void Mul(float m){x*=m;y*=m;} 17 void Mul(float xi,float yi){x*=xi;y*=yi;} 18 void Sub(float m){x-=m;y-=m;} 19 void Sub(float xi,float yi){x-=xi;y-=yi;} 20 21 float Length(); 22 CVec2D Normalize(); 23 float Dot(CVec2D); 24 25 float x,y,z,w; 26}; 27CVec2D::CVec2D(){Clear();} 28CVec2D::CVec2D(float xi,float yi){Clear();Set(xi,yi);} 29 30float CVec2D::Length(){ 31 return sqrt(x*x+y*y); 32} 33CVec2D CVec2D::Normalize(){ 34 CVec2D temp_vec; 35 float l = this->Length(); 36 if(l!=0){ 37 temp_vec.x = x/l; 38 temp_vec.y = y/l; 39 } 40 return temp_vec; 41} 42float CVec2D::Dot(CVec2D vec){ 43 return (x*vec.x +y*vec.y); 44} 45void CVec2D::Add(CVec2D v){ 46 x+=v.x; 47 y+=v.y; 48} 49 50 51class CPenguin_Body { 52public: 53 CPenguin_Body(float,float,float); 54 ~CPenguin_Body(){} 55 56 void Draw(); 57 void Update(); 58 void MouseGrab(); 59 60 CVec2D position; 61 CVec2D velocity; 62 CVec2D acceleration; 63 64 void Bound(); 65 66 bool CheckCollision(CPenguin_Body*); 67 bool Check_Collision(std::vector<CPenguin_Body*>); 68 bool collision; 69 float radius; 70 float mass; 71 72 int id; 73 74 bool draw_full; 75 76 static int new_UID; 77}; 78CPenguin_Body::CPenguin_Body(float x,float y,float r){ 79 position.x = x; 80 position.y = y; 81 velocity.Set(0.0f,0.0f); 82 acceleration.Set(0.0f,0.0f); 83 radius = r; 84 id = (new_UID++); 85 printf("ID : %i\n",id); 86 collision = false; 87 88 draw_full = false; 89 90 mass = 1.0f; 91} 92void CPenguin_Body::Draw(){ 93 if(collision)al_draw_circle(position.x,position.y,radius,al_map_rgb(255,0,0),1); 94 else al_draw_circle(position.x,position.y,radius,al_map_rgb(255,255,255),1); 95 //al_draw_line(position.x,position.y,position.x+100,position.y,al_map_rgb(255,0,0),1); 96 97 if(draw_full){ 98 al_draw_filled_circle(position.x,position.y,radius,al_map_rgb(255,0,255)); 99 } 100} 101void CPenguin_Body::Update(){ 102 103 acceleration.Set(0.0,0.0); 104 105 velocity.Add(acceleration); 106 position.Add(velocity); 107 acceleration.Clear(); 108} 109 110bool CPenguin_Body::CheckCollision(CPenguin_Body * other_circle){ 111 float x1 = position.x; 112 float y1 = position.y; 113 float r1 = radius; 114 115 float x2 = other_circle->position.x; 116 float y2 = other_circle->position.y; 117 float r2 = other_circle->radius; 118 119 float distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); 120 float radii = r1+r2; 121 122 123 if(abs(distance)<radii){ 124 125 collision = true; 126 127 return true; 128 } else { 129 collision=false; 130 return false; 131 } 132 133} 134 135void CPenguin_Body::MouseGrab(){ 136 if(mouse_lmb){ 137 position.x = mouse_x; 138 position.y = mouse_y; 139 } 140} 141 142void CPenguin_Body::Bound(){ 143 if(position.y>=860){ 144 position.SetY(860); 145 //velocity.Mul(-1.0f); 146 velocity.SetY((velocity.y*-1.0f)); 147 acceleration.SetY(5.0f); 148 //drag 149 } 150 if(position.y<0){ 151 position.SetY(1); 152 //velocity.Mul(-1.0f); 153 velocity.SetY((velocity.y*-1.0f)); 154 acceleration.SetY(-5.0f); 155 } 156 if(position.x<0){ 157 position.SetX(0); 158 //velocity.Mul(-1.0f); 159 velocity.SetX((velocity.x*-1.0f)); 160 } 161 if(position.x>1180){ 162 position.SetX(1180); 163 //velocity.Mul(-1.0f); 164 velocity.SetX((velocity.x*-1.0f)); 165 //acceleration.SetX(5.0f); 166 } 167} 168int CPenguin_Body::new_UID = 0; 169 170class CPenguin_Collision { 171public: 172 CPenguin_Collision(); 173 ~CPenguin_Collision(){} 174 175 void Check(std::vector<CPenguin_Body*>); 176 void Check(std::vector<CPenguin_Body*>,CPenguin_Body*); 177 void Add(CPenguin_Body*,CPenguin_Body*); 178 void Clear(); 179 void Resolve(); 180 181 std::vector<CPenguin_Body*>body_a; 182 std::vector<CPenguin_Body*>body_b; 183}; 184CPenguin_Collision::CPenguin_Collision(){} 185void CPenguin_Collision::Check(std::vector<CPenguin_Body*>body){ 186 for(unsigned int i=0;i<body.size();i++){ 187 for(unsigned int j=0;j<body.size();j++){ 188 if(body[i]->id != body[j]->id){ 189 if(body[i]->CheckCollision(body[j])){ 190 Add(body[i],body[j]); 191 } 192 } 193 } 194 } 195} 196void CPenguin_Collision::Check(std::vector<CPenguin_Body*>body,CPenguin_Body* p){ 197 for(unsigned int i=0;i<body.size();i++){ 198 if(p->id != body[i]->id){ 199 if(p->CheckCollision(body[i])){ 200 Add(p,body[i]); 201 } 202 } 203 } 204} 205void CPenguin_Collision::Add(CPenguin_Body * a,CPenguin_Body * b){ 206 body_a.push_back(a); 207 body_b.push_back(b); 208 printf("Collision Added\n"); 209} 210void CPenguin_Collision::Clear(){ 211 body_a.clear(); 212 body_b.clear(); 213 printf("VECTOR CLEARED %i\n",body_a.size()); 214} 215void CPenguin_Collision::Resolve(){ 216 //Same function in main for collision resolution 217 for(unsigned int i=0;i<body_a.size();i++){ 218 float x1 = body_a[i]->position.x; 219 float y1 = body_a[i]->position.y; 220 float r1 = body_a[i]->radius; 221 222 float x2 = body_b[i]->position.x; 223 float y2 = body_b[i]->position.y; 224 float r2 = body_b[i]->radius; 225 float distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); 226 float overlap_distance = 0.5 * (distance - r1 - r2); 227 228 body_a[i]->position.x += overlap_distance * (body_a[i]->position.x - body_b[i]->position.x) / distance; 229 body_a[i]->position.y += overlap_distance * (body_a[i]->position.y - body_b[i]->position.y) / distance; 230 231 body_b[i]->position.x -= overlap_distance * (body_b[i]->position.x - body_a[i]->position.x) / distance; 232 body_b[i]->position.y -= overlap_distance * (body_b[i]->position.y - body_a[i]->position.y) / distance; 233 234 float nx = (x2 - x1) / distance; 235 float ny = (y2 - y1) / distance; 236 237 float tx = -ny; 238 float ty = nx; 239 240 float dpTan1 = body_a[i]->velocity.x * tx + body_a[i]->velocity.y * ty; 241 float dpTan2 = body_b[i]->velocity.x * tx + body_b[i]->velocity.y * ty; 242 243 float dpNorm1 = body_a[i]->velocity.x * nx + body_a[i]->velocity.y * ny; 244 float dpNorm2 = body_b[i]->velocity.x * nx + body_b[i]->velocity.y * ny; 245 246 float m1 = (dpNorm1 * (body_a[i]->mass - body_b[i]->mass) + 2.0f * body_b[i]->mass * dpNorm2) / (body_a[i]->mass + body_b[i]->mass); 247 float m2 = (dpNorm2 * (body_b[i]->mass - body_a[i]->mass) + 2.0f * body_a[i]->mass * dpNorm1) / (body_a[i]->mass + body_b[i]->mass); 248 249 body_a[i]->velocity.x = tx * dpTan1 + nx * m1; 250 body_a[i]->velocity.y = ty * dpTan1 + ny * m1; 251 252 body_b[i]->velocity.x = tx * dpTan2 + nx * m2; 253 body_b[i]->velocity.y = ty * dpTan2 + ny * m2; 254 255 printf("Collision Resolved\n"); 256 } 257 if(!body_a.empty())Clear(); 258} 259 260class CPenguin2D { 261public: 262 CPenguin2D(); 263 ~CPenguin2D(); 264 265 void Draw(); 266 void Update(); 267 268 CPenguin_Collision * penguin_collision; 269 CPenguin_Body * c1,*c2; 270 271 std::vector<CPenguin_Body*>my_body; 272}; 273CPenguin2D::CPenguin2D(){ 274 //stand alone body objects are working 275 c1 = new CPenguin_Body(240,600,10); 276 c2 = new CPenguin_Body(240,200,100); 277 c1->velocity.Set(0.1,-20.0); 278 c2->mass = 10.0; 279 280 //body objects in this vector are not 281 CPenguin_Body * b1 = new CPenguin_Body(700,100,100); 282 my_body.push_back(b1); 283 b1->velocity.x=1.0; 284 b1->draw_full=true; 285 CPenguin_Body * b2 = new CPenguin_Body(300,100,50); 286 b2->velocity.x=10.0; 287 b2->velocity.y=10; 288 b2->draw_full=true; 289 my_body.push_back(b2); 290 291 penguin_collision = new CPenguin_Collision(); 292} 293CPenguin2D::~CPenguin2D(){} 294 295void CPenguin2D::Draw(){ 296 c1->Draw(); 297 c2->Draw(); 298 299 for(unsigned int i=0;i<my_body.size();i++){ 300 my_body[i]->Draw(); 301 } 302} 303void CPenguin2D::Update(){ 304 c1->Update(); 305 c2->Update(); 306 c1->Bound(); 307 c2->Bound(); 308 309 my_body[0]->MouseGrab(); 310 311 for(unsigned int i=0;i<my_body.size();i++){ 312 my_body[i]->Update(); 313 my_body[i]->Bound(); 314 penguin_collision->Check(my_body,my_body[i]); 315 } 316 317 //this function should do the same as if(c1->CheckCollision(c2)){ below, but doesn't 318 penguin_collision->Resolve(); 319 320 //this function works (almost) as intended 321 if(c1->CheckCollision(c2)){ 322 float x1 = c1->position.x; 323 float y1 = c1->position.y; 324 float r1 = c1->radius; 325 326 float x2 = c2->position.x; 327 float y2 = c2->position.y; 328 float r2 = c2->radius; 329 float distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); 330 float overlap_distance = 0.5 * (distance - r1 - r2); 331 332 c1->position.x += overlap_distance * (c1->position.x - c2->position.x) / distance; 333 c1->position.y += overlap_distance * (c1->position.y - c2->position.y) / distance; 334 335 c2->position.x -= overlap_distance * (c2->position.x - c1->position.x) / distance; 336 c2->position.y -= overlap_distance * (c2->position.y - c1->position.y) / distance; 337 338 float nx = (x2 - x1) / distance; 339 float ny = (y2 - y1) / distance; 340 341 float tx = -ny; 342 float ty = nx; 343 344 float dpTan1 = c1->velocity.x * tx + c1->velocity.y * ty; 345 float dpTan2 = c2->velocity.x * tx + c2->velocity.y * ty; 346 347 float dpNorm1 = c1->velocity.x * nx + c1->velocity.y * ny; 348 float dpNorm2 = c2->velocity.x * nx + c2->velocity.y * ny; 349 350 float m1 = (dpNorm1 * (c1->mass - c2->mass) + 2.0f * c2->mass * dpNorm2) / (c1->mass + c2->mass); 351 float m2 = (dpNorm2 * (c2->mass - c1->mass) + 2.0f * c1->mass * dpNorm1) / (c1->mass + c2->mass); 352 353 c1->velocity.x = tx * dpTan1 + nx * m1; 354 c1->velocity.y = ty * dpTan1 + ny * m1; 355 356 c2->velocity.x = tx * dpTan2 + nx * m2; 357 c2->velocity.y = ty * dpTan2 + ny * m2; 358 359 } 360} 361 362CPenguin2D * PENGUIN_2D;

If anyone has any ideas I would be eternally grateful, this has been driving me nuts for a while. I'm pretty sure it's the collision class that's cocking things up, or maybe where I'm checking for the collisions. I've written two collision Check() functions and both do seem to do the same thing. Basically I need a way of storing the body objects in a vector, iterating though, updating and checking for collisions, then adding these collisions to another vector and resolving them. Or put more simply, I need the filled balls to act like the unfilled balls, but more object orientated. Thanks

dthompson
Member #5,749
April 2005
avatar

Looks like you've got the right idea; circular collision boils down to:

#SelectExpand
1// say you're storing positions and their radii: 2typedef struct CIRCLE 3{ 4 float x, y, r; 5} CIRCLE; 6 7bool circular_collision(CIRCLE* a, CIRCLE* b) 8{ 9 // calculate distance between the two points 10 float distance = sqrtf(powf(b->x - a->x, 2) + powf(b->y - a->y, 2)); 11 if(distance < 0.0f) 12 distance *= -1; 13 14 // if the distance is less than the sum of the radii, they're colliding 15 return distance < (a->r + b->r); 16}

You've posted quite a lot of code; have you tried isolating the problem using some tests?

The only potential issue I can see at a glance is your usage of abs() - if this is cmath's function, you'll lose precision here - though this probably won't matter if you're not concerned about subpixel accuracy.

______________________________________________________
Website. It was freakdesign.bafsoft.net.
This isn't a game!

dakatt
Member #10,695
February 2009

Edit I made a short video of the problem. The white circles are c1 and c2 and work for the most part. The pink circles are contained in a vector and use the penguin_collision class. They only interact with their respective colours.

https://www.youtube.com/watch?v=z0KNHp4H7VM&feature=youtu.be

Yeah I apologize for the amount of code, I thought I'd post all of it because I think the problem may be in how it's structured. The collision itself seems to be ok, it's getting the balls to bounce properly. The objects outside of the vector container do this, but in order to check every collision I would need to individually and manually check each object against every other object. I've tried to do this using a collision class which takes a vector of objects that records and "resolves" the collisions but the objects have unpredictable behaviour, most notably they seem to attract each other upon collision (and then all kinds of craziness happens). Since I'm using the same function for resolving the collision I can only assume the problem is with this collision class.

#SelectExpand
1class CPenguin_Collision { 2public: 3 CPenguin_Collision(); 4 ~CPenguin_Collision(){} 5 6 void Check(std::vector<CPenguin_Body*>); 7 void Check(std::vector<CPenguin_Body*>,CPenguin_Body*); 8 void Add(CPenguin_Body*,CPenguin_Body*); 9 void Clear(); 10 void Resolve(); 11 12 std::vector<CPenguin_Body*>body_a; 13 std::vector<CPenguin_Body*>body_b; 14}; 15CPenguin_Collision::CPenguin_Collision(){} 16void CPenguin_Collision::Check(std::vector<CPenguin_Body*>body){ 17 for(unsigned int i=0;i<body.size();i++){ 18 for(unsigned int j=0;j<body.size();j++){ 19 if(body[i]->id != body[j]->id){ 20 if(body[i]->CheckCollision(body[j])){ 21 Add(body[i],body[j]); 22 } 23 } 24 } 25 } 26} 27void CPenguin_Collision::Check(std::vector<CPenguin_Body*>body,CPenguin_Body* p){ 28 for(unsigned int i=0;i<body.size();i++){ 29 if(p->id != body[i]->id){ 30 if(p->CheckCollision(body[i])){ 31 Add(p,body[i]); 32 } 33 } 34 } 35} 36void CPenguin_Collision::Add(CPenguin_Body * a,CPenguin_Body * b){ 37 body_a.push_back(a); 38 body_b.push_back(b); 39 printf("Collision Added\n"); 40} 41void CPenguin_Collision::Clear(){ 42 body_a.clear(); 43 body_b.clear(); 44 printf("VECTOR CLEARED %i\n",body_a.size()); 45} 46void CPenguin_Collision::Resolve(){ 47 //Same function in main for collision resolution 48 for(unsigned int i=0;i<body_a.size();i++){ 49 float x1 = body_a[i]->position.x; 50 float y1 = body_a[i]->position.y; 51 float r1 = body_a[i]->radius; 52 53 float x2 = body_b[i]->position.x; 54 float y2 = body_b[i]->position.y; 55 float r2 = body_b[i]->radius; 56 float distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); 57 float overlap_distance = 0.5 * (distance - r1 - r2); 58 59 body_a[i]->position.x += overlap_distance * (body_a[i]->position.x - body_b[i]->position.x) / distance; 60 body_a[i]->position.y += overlap_distance * (body_a[i]->position.y - body_b[i]->position.y) / distance; 61 62 body_b[i]->position.x -= overlap_distance * (body_b[i]->position.x - body_a[i]->position.x) / distance; 63 body_b[i]->position.y -= overlap_distance * (body_b[i]->position.y - body_a[i]->position.y) / distance; 64 65 float nx = (x2 - x1) / distance; 66 float ny = (y2 - y1) / distance; 67 68 float tx = -ny; 69 float ty = nx; 70 71 float dpTan1 = body_a[i]->velocity.x * tx + body_a[i]->velocity.y * ty; 72 float dpTan2 = body_b[i]->velocity.x * tx + body_b[i]->velocity.y * ty; 73 74 float dpNorm1 = body_a[i]->velocity.x * nx + body_a[i]->velocity.y * ny; 75 float dpNorm2 = body_b[i]->velocity.x * nx + body_b[i]->velocity.y * ny; 76 77 float m1 = (dpNorm1 * (body_a[i]->mass - body_b[i]->mass) + 2.0f * body_b[i]->mass * dpNorm2) / (body_a[i]->mass + body_b[i]->mass); 78 float m2 = (dpNorm2 * (body_b[i]->mass - body_a[i]->mass) + 2.0f * body_a[i]->mass * dpNorm1) / (body_a[i]->mass + body_b[i]->mass); 79 80 body_a[i]->velocity.x = tx * dpTan1 + nx * m1; 81 body_a[i]->velocity.y = ty * dpTan1 + ny * m1; 82 83 body_b[i]->velocity.x = tx * dpTan2 + nx * m2; 84 body_b[i]->velocity.y = ty * dpTan2 + ny * m2; 85 86 printf("Collision Resolved\n"); 87 } 88 if(!body_a.empty())Clear(); 89}

So this class takes a vector of body objects, Check()'s to see if any objects are colliding, if they are it Add()'s the two objects in to the vector body_a[] and body_b[], and (in theory) Resolve()'s the collisions by running through the vectors in pairs before finally Clear()'ing body_a[] and body_b[] ready for the next loop. Essentially I'm trying to do this -

#SelectExpand
1main_loop(){ 2 c1->Update(); //just position updates 3 c2->Update(); 4 c1->Bound(); //keeps objects on screen 5 c2->Bound(); 6 7 if(c1->CheckCollision(c2)){ //checks collision between 2 objects 8 float x1 = c1->position.x; 9 float y1 = c1->position.y; 10 float r1 = c1->radius; 11 12 float x2 = c2->position.x; 13 float y2 = c2->position.y; 14 float r2 = c2->radius; 15 float distance = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); 16 float overlap_distance = 0.5 * (distance - r1 - r2); 17 18 c1->position.x += overlap_distance * (c1->position.x - c2->position.x) / distance; 19 c1->position.y += overlap_distance * (c1->position.y - c2->position.y) / distance; 20 21 c2->position.x -= overlap_distance * (c2->position.x - c1->position.x) / distance; 22 c2->position.y -= overlap_distance * (c2->position.y - c1->position.y) / distance; 23 24 float nx = (x2 - x1) / distance; 25 float ny = (y2 - y1) / distance; 26 27 float tx = -ny; 28 float ty = nx; 29 30 float dpTan1 = c1->velocity.x * tx + c1->velocity.y * ty; 31 float dpTan2 = c2->velocity.x * tx + c2->velocity.y * ty; 32 33 float dpNorm1 = c1->velocity.x * nx + c1->velocity.y * ny; 34 float dpNorm2 = c2->velocity.x * nx + c2->velocity.y * ny; 35 36 float m1 = (dpNorm1 * (c1->mass - c2->mass) + 2.0f * c2->mass * dpNorm2) / (c1->mass + c2->mass); 37 float m2 = (dpNorm2 * (c2->mass - c1->mass) + 2.0f * c1->mass * dpNorm1) / (c1->mass + c2->mass); 38 39 c1->velocity.x = tx * dpTan1 + nx * m1; 40 c1->velocity.y = ty * dpTan1 + ny * m1; 41 42 c2->velocity.x = tx * dpTan2 + nx * m2; 43 c2->velocity.y = ty * dpTan2 + ny * m2; 44 45 } 46}

But object orientated. I don't think individually checking each objects collision against every other object is "sustainable".

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I can think of one problem. Every time you move an object you have to check it for collision with every other object again, otherwise you can end up with overlaps. Same goes for moving objects on the edge of the screen. You might have moved them onto another object.

When I did a circle collision demo, I used intercept times based on relative velocity. To scale it up, you need to build a collision table, of (N^2 + N)/2 size. Then you store the intercept times in the table, and react to them in order. To make things easier, don't move an object if it's way is obstructed. Having to move objects back out of another object sucks. Just don't let them overlap in the first place.

Some simple geometry and you can figure out whether a collision is even possible by using 2 line vs circle collisions. Or compute angular bounds of collision. Whatever.

I can maybe help you more with this if you're interested. I can't quite follow all the code you posted, but I didn't look that closely.

If you're interested in me actually checking out the code, post a compilable zip file with all the resources necessary and I will try it and maybe debug it a little bit if I get bored.

Peter Hull
Member #1,136
March 2001

This is quite a tricky problem and if I remember correctly, in the general case, you need to split each time step - calculate the 'sub-step time' to the first collision, separate the colliding bodies, re-compute velocities and repeat. Do some googling for 'Erin Catto' he did some good lectures on this.
For your case you are double-counting your collisions, body A on B and B on A.
I changed your code (line 185 in your first listing) to

void CPenguin_Collision::Check(std::vector<CPenguin_Body*>body){
  for(unsigned int i=0;i<body.size();i++){
    for(unsigned int j=0; j<i; j++){
      if(body[i]->id != body[j]->id){
        if(body[i]->CheckCollision(body[j])){
          Add(body[i],body[j]);
        }
      }
    }
  }
}

and (lines 311-315)

  for(unsigned int i=0;i<my_body.size();i++){
    my_body[i]->Update();
    my_body[i]->Bound();
  }
  penguin_collision->Check(my_body);

and it looked 'better' to me on a first glance.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

dakatt
Member #10,695
February 2009

I changed the abs to fabs dbthompson. It may just be me but the circles don't seem to stick to each other as much. It happens maybe every 20 collisions now.

I can see that problem with overlaps Edgar, tbh I can barely understand the code I have at the moment but I will check out those guides. Is it that much more coding in general or more about understanding the concepts? I'm not sure whether the circles are getting stuck because of overlap but it seems to happen at random. I was hoping it was where my collision or overlap detection was a bit off or missing some calculation. Would the overlap happen with just two objects?

That's was exactly the problem Peter, I realised this morning I was doubling up one of the position updates although I still had no idea how to fix it. Your edit seems to have done the trick.

Also one last question, are there any small changes I can make to improve the code any more or anything I can do to improve my coding in general? I'm probably breaking a million and one conventions and rules but I'd like to get better.

Thank you again for all your help guys

Peter Hull
Member #1,136
March 2001

dakatt said:

Also one last question, are there any small changes I can make to improve the code any more

Two things I noticed:

  • I would recommend avoiding passing a vector (or any container) by value (line 67) because that makes a copy every time. This is inefficient and also loses any changes you might make to the container in the body of the function. A reference or constant reference is better, unless a copy is what you really want.

  • The result of sqrt is always positive so you don't need abs in line 123

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

This is quite a tricky problem and if I remember correctly, in the general case, you need to split each time step - calculate the 'sub-step time' to the first collision, separate the colliding bodies, re-compute velocities and repeat. Do some googling for 'Erin Catto' he did some good lectures on this.

Basically, you're doing a binary search over time to find the first collision time.

Something you can do to make objects stop 'sticking' to each other is to allow overlap. If c1.Future(0.0).Overlaps(c2.Future(0.0)), no collision.

dakatt said:

I can see that problem with overlaps Edgar, tbh I can barely understand the code I have at the moment but I will check out those guides. Is it that much more coding in general or more about understanding the concepts? I'm not sure whether the circles are getting stuck because of overlap but it seems to happen at random. I was hoping it was where my collision or overlap detection was a bit off or missing some calculation. Would the overlap happen with just two objects?

Given that you have 'boundaries' that move objects, overlap can happen anytime two objects follow each other toward the edge. The collision with the edge is still a collision.

Basically, overlap can happen any time you move an object. The solution then is to never move your object by a delta time greater than the first possible collision solution. A collision should never change the position of an object, only it's velocity or momentum or impulse. Because a collision is instantaneous transferral of energy. The objects don't have time to move, and they wouldn't move until the energy transfer is complete.

This is why intercept tables save you all this trouble.

Read the guides I put up, they're very simple, and self explanatory, with heavy description.

https://github.com/liballeg/allegro_wiki/wiki/Circle-V-Circle-collision

I'm currently working on a demo program and a collision table that will save you many many headaches.

As for code advice, I would recommend you work on your vector class and your circle class. The vector class could really use operator overloading, look into it, and the circle class needs methods for things like future position, and Update(double dt).

UPDATE

I finished a small demo you can try. src and win32 binary are included.

CVC.zip

{"name":"611751","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/c\/fcf0cd94f1968d5d6580b01040a57fc9.png","w":1026,"h":801,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/c\/fcf0cd94f1968d5d6580b01040a57fc9"}611751

Audric
Member #907
January 2001

Tested, works fine.
I'm not used to friction-less physics, but it does seem to be more or less of a natural movement.
One surprising behavior is in small-large collision : a slow-moving large ball hitting a slow-moving small ball will hurl the latter at a surprising velocity. The energy sum is probably unchanged, but I feel the amount transferred is more than expected.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I forgot to say the controls are LMB to launch a small ball, RMB to launch a large ball, and R to toggle screen clearing (that way you can see the trails).

Balls that overlap are shown in red.

Audric said:

One surprising behavior is in small-large collision : a slow-moving large ball hitting a slow-moving small ball will hurl the latter at a surprising velocity. The energy sum is probably unchanged, but I feel the amount transferred is more than expected.

Yeah, I don't know actual physics. All I did was transfer the normal momentum to the other ball. A larger ball has more mass, so more energy is injected into the smaller ball, which results in higher velocities.

What would be more natural? Did you have some equations in mind?

Go to: