2

Ok, to start off with, first post (with a large issue that I don't know why it's happening nor which part of the code is getting messed up), so if I do anything wrong, please let me know what it is, and how to fix it for the future. Also, I am rather new to C++ and FLTK, so my code most definitely isn't the best.

The code is written in C++ and using FLTK to render it.

Now, what I am trying to make is a physics simulation between many circles in an attempt to imitate something like liquid fun, where it acts somewhat like water(or for me, a bunch of solid circles colliding) What actually happens is halfway correct. The physics looks ok at times, but what ends up happening is a few things first, when there are many circles sitting on top of each other(I don't mean they are occupying the same space, more like throwing a bunch of balls in a box and them sitting on top of one another), they don't really like it.

1) For some reason, they start to phase through and into each other, then try to find a position where they can be, and making the whole pile freak out as it tries to find an open space.

2) They don't like walls. I don't think(especially corners where most issues seem to happen), which seems to, in corners at least, make the entire layer freak out(if I have a single layer thick of circles on the bottom and one gets pushed too far into a corner, it freaks out, then the rest freak out, and suddenly they are all going REALLY fast side to side).

3) They don't ever stop moving if they are piled on top of one another, they are constantly jittering or moving I have tried several different circle intersection codes from online, and the one I am using now seems to be the most reliable for making sure that circles generally don't try to occupy the same space, but the circles are freaking out when they don't have a choice(for whatever reason, the ones above them don't move out of the way, they just continue to press on the ones below).

At this point, I don't know what I am doing right, and what I am doing so wrong that has led me to this point, and it's just frustrating me

Note for the code: I am using a multidimensional array to store all the data for the circles called cd[8][C] where C is the circle that I specifically want to know the data of; cd[0][C] is the radius of circle C; cd[1][C] is the mass of circle C; cd[2][C] is the X coordinate of circle C, and cd[3][C] is the Y coordinate; cd[4][C] and cd[5][C] is the X and Y velocities of circle C; cd[6][C] and cd[7][C] are unused as of right now

Now for the mess of code and I am so sorry for the mess it truly is, even after the about hour I spent cleaning it up and tidying it up If anyone can tell me good tips for making this better and prettier for the future let me know!

First I will include the main portion of the script; this part is called from the main run loop and does pretty much everything:

void math() {
collcount = 0;
    for (int i = 0; i < C; ++i) {
        addx1 = 0;
        addy1 = 0;
    //if circle is not registered as fixed
    if (cd[6][i] != 1) {
        for (int i2 = 0; i2 < C; ++i2) {
        //if circle i2 is not the same as the circle i
        if (i2 != i) {
        //check for collisions between circle i and i2
        collide_check(i,i2);
        //if collision detected between circles i and i2
            if (coltf == 1) {
                    //add collision pairs to list to calculate moving them out of eachother later
                    coll[0][collcount] = i;
                    coll[1][collcount] = i2;
                    //run collision maths
                    collide_calc(i,i2);
                    collcount = collcount + 1;      
                    }
                }
            }
        }
    //set temp velocities to new velocity if object collided ever, or keep it the same if it didnt
    if (addx1 == 0 && addy1 == 0) {
        vx[i]=cd[4][i];
        vy[i]=cd[5][i];
    }else {
        vx[i] = addx1;
        vy[i] = addy1;
    }
}
//calculate and move colliding points so they are no longer colliding
for (int i = 0; i < collcount; ++i) {
if (cd[6][coll[0][i]] != 1) {
                        addx1 = 0;
                        addy1 = 0;
                        collide_check(coll[0][i],coll[1][i]);
                        cd[2][coll[0][i]] = cd[2][coll[1][i]]+(cd[0][coll[0][i]]+cd[0][coll[1][i]]+1)*(ccxdis/cdis);
                        cd[3][coll[0][i]] = cd[3][coll[1][i]]+(cd[0][coll[0][i]]+cd[0][coll[1][i]]+1)*(ccydis/cdis);
                    }
}
//apply new velocities
for (int i = 0; i < C; ++i) {
    cd[4][i] = vx[i];
    cd[5][i] = vy[i];
}
//wall collision calculator
for (int i = 0; i < C; ++i) {
if (cd[2][i] > btx+bw) {
                cd[4][i] = -0.9*cd[4][i];
                cd[2][i] = btx+bw;
            }else if (cd[2][i] < btx) {
                cd[4][i] = -0.9*cd[4][i];
                cd[2][i] = btx;
            }
            if (cd[3][i] > bty+bh) {
                cd[5][i] = -0.9*cd[5][i];
                cd[3][i] = bty+bh;
            }else if (cd[3][i] < bty) {
                cd[5][i] = -0.9*cd[5][i];
                cd[3][i] = bty;
            }
        }
//end of math(); function
}

next, here is the part that calculates whether or not a collision is occuring between circles i and i2:

void collide_check(int c1, int c2) {
ccxdis = cd[2][c1]-cd[2][c2];
ccydis = cd[3][c1]-cd[3][c2];
cdis = sqrt(ccxdis*ccxdis+ccydis*ccydis);
movdis = (cd[0][c1]+cd[0][c1])-cdis;
//calculate if collide is true
if (cdis < cd[0][c2]+cd[0][c1]) {
    coltf = 1;
} else {coltf = 0;}
}

Then finally the part that calculates the final velocities of collisions:

void collide_calc(int c1, int c2) {
//current collision math script
addx1 = addx1+1*(cd[4][c1]*(cd[1][c1]-cd[1][c2])+(2*cd[1][c2]*cd[4][c2]))/(cd[1][c1]+cd[1][c2]);
addy1 = addy1+1*(cd[5][c1]*(cd[1][c1]-cd[1][c2])+(2*cd[1][c2]*cd[5][c2]))/(cd[1][c1]+cd[1][c2]);
}

Heres the full code, and I shrank it about as much as I could, going from 400 lines to about 160; If I did this wrong, or I should share my full code for others to test differently, please let me know! Thanks!

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/fl_draw.H>
#include <math.h>
using namespace std;
//number of circles spawned
const int C = 19;
//data defining all circles
float cd[8][C] = {
{10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},
{5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},
{100,110,120,130,140,150,160,170,180,120,140,160,180,425,450,475,430,455,480},
{600,590,600,590,600,590,600,590,600,580,580,580,580,600,600,600,570,570,570},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
//replace above data with below data to see problem 2
/*
{10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10},
{5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5},
{100,125,150,175,200,225,250,275,300,325,350,375,400,425,450,475,500,525,550},
{600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600},
{-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
*/
int coll[2][100];
float vx[C];
float vy[C];
int btx = 50;
int bty = btx;
int bw = 800;
int bh = 600;
int coltf, test1, collcount;
float addx1, addy1, cdis, movdis, ccydis, ccxdis, ccxvel, ccyvel;

void collide_check(int c1, int c2) {
ccxdis = cd[2][c1]-cd[2][c2];
ccydis = cd[3][c1]-cd[3][c2];
cdis = sqrt(ccxdis*ccxdis+ccydis*ccydis);
movdis = (cd[0][c1]+cd[0][c1])-cdis;
//calculate if collide is true
if (cdis < cd[0][c2]+cd[0][c1]) {
    coltf = 1;
} else {coltf = 0;}
}

void collide_calc(int c1, int c2) {
//current collision math script
addx1 = addx1+1*(cd[4][c1]*(cd[1][c1]-cd[1][c2])+(2*cd[1][c2]*cd[4][c2]))/(cd[1][c1]+cd[1][c2]);
addy1 = addy1+1*(cd[5][c1]*(cd[1][c1]-cd[1][c2])+(2*cd[1][c2]*cd[5][c2]))/(cd[1][c1]+cd[1][c2]);
}

void math() {
collcount = 0;
    for (int i = 0; i < C; ++i) {
        addx1 = 0;
        addy1 = 0;
    //if circle is not registered as fixed
    if (cd[6][i] != 1) {
        for (int i2 = 0; i2 < C; ++i2) {
        //if circle i2 is not the same as the circle i
        if (i2 != i) {
        //check for collisions between circle i and i2
        collide_check(i,i2);
        //if collision detected between circles i and i2
            if (coltf == 1) {
                    //add collision pairs to list to calculate moving them out of eachother later
                    coll[0][collcount] = i;
                    coll[1][collcount] = i2;
                    //run collision maths
                    collide_calc(i,i2);
                    collcount = collcount + 1;      
                    }
                }
            }
        }
    //set temp velocities to new velocity if object collided ever, or keep it the same if it didnt
    if (addx1 == 0 && addy1 == 0) {
        vx[i]=cd[4][i];
        vy[i]=cd[5][i];
    }else {
        vx[i] = addx1;
        vy[i] = addy1;
    }
}
//calculate and move colliding points so they are no longer colliding
for (int i = 0; i < collcount; ++i) {
if (cd[6][coll[0][i]] != 1) {
                        addx1 = 0;
                        addy1 = 0;
                        collide_check(coll[0][i],coll[1][i]);
                        cd[2][coll[0][i]] = cd[2][coll[1][i]]+(cd[0][coll[0][i]]+cd[0][coll[1][i]]+1)*(ccxdis/cdis);
                        cd[3][coll[0][i]] = cd[3][coll[1][i]]+(cd[0][coll[0][i]]+cd[0][coll[1][i]]+1)*(ccydis/cdis);
                    }
}
//apply new velocities
for (int i = 0; i < C; ++i) {
    cd[4][i] = vx[i];
    cd[5][i] = vy[i];
}
//wall collision calculator
for (int i = 0; i < C; ++i) {
if (cd[2][i] > btx+bw) {
                cd[4][i] = -0.9*cd[4][i];
                cd[2][i] = btx+bw;
            }else if (cd[2][i] < btx) {
                cd[4][i] = -0.9*cd[4][i];
                cd[2][i] = btx;
            }
            if (cd[3][i] > bty+bh) {
                cd[5][i] = -0.9*cd[5][i];
                cd[3][i] = bty+bh;
            }else if (cd[3][i] < bty) {
                cd[5][i] = -0.9*cd[5][i];
                cd[3][i] = bty;
            }
        }
//end of math(); function
}

class Drawing : public Fl_Widget {
  public:
    Drawing(int X,int Y,int W,int H) : Fl_Widget(X,Y,W,H) {}
  private:
    void draw() {
        fl_color(FL_RED);
        fl_line_style(0,5); 
            //draw circles and velocity vectors 
            for (int i = 0; i < C; ++i)
            {
                fl_color(FL_RED);
            fl_circle(cd[2][i],cd[3][i],cd[0][i]);  
            }
            //draw boundaries
            fl_line(btx,bty,btx,bty+bh);
            fl_line(btx,bty+bh,btx+bw,bty+bh);
            fl_line(btx+bw,bty+bh,btx+bw,bty);
            fl_line(btx+bw,bty,btx,bty);
        fl_line_style(0);
    }
};
//main updating/time step portion
void upda(void*) {
    for (int i = 0; i < C; ++i) {
    cd[5][i] = cd[5][i]+1;
    cd[2][i] = cd[4][i]+cd[2][i];
    cd[3][i] = cd[5][i]+cd[3][i];
    }
    math();
    Fl::redraw();
    Fl::repeat_timeout(0.01, upda);
}
int main() {
    Fl_Window *window = new Fl_Window(1000,1000,"FLTK drawing example");
    Drawing canvas(10,10,1000,1000);
 Fl::add_timeout(0.01,upda);
    window->end();
    window->show();  
    return Fl::run();
}
Pjbomb2
  • 21
  • 3
  • Not a bad first question. You can probably improve it by constructing a [mcve]. The [mcve] makes it easy for those of us out here to run the program, analyze it, and and kick it around for a while to see what falls off. The true beauty of the [mcve] is making one reduces the code to nothing but the code absolutely necessary to reproduce the bug. This gives the bug very little room to hide and almost always results in you finding the bug yourself. If you make the [mcve] before asking the question most of the time you eliminate the need to ask the question. – user4581301 Jan 17 '20 at 19:16
  • ok! will do! I didnt know about this! should I include the rendering system? it uses FLTK for the rendering of everything, so im curious if that should be included in the example – Pjbomb2 Jan 17 '20 at 20:05
  • also also, should the piece of code that allows the user to move a ball to a different location stay in? – Pjbomb2 Jan 17 '20 at 20:32
  • What you want is the smallest possible code that produces the bug. Since you've got a lot of set-up and tear-down to get the graphics going, your initial [mcve] will be quite long. The thing is, the problem's not going to be in the graphics, it's going to be in the math. If you can eliminate the graphics and focus on the math, you'll have a stronger, tighter example. – user4581301 Jan 17 '20 at 20:41
  • A common problem is checking after the collision has already taken place. If you move object A and object B to their new coordinates and then test if they have collided, it's too late. They're on top of each other and you have to clean up. Worse they could have completely already passed through each other without getting caught. – user4581301 Jan 17 '20 at 20:42
  • The graphics I am not 100% sure how to get rid of, as it is important in knowing whats happening, as it can be good in some positions(if none of the balls are really touching) but bad in others as for the collision, what I do is I move everything, check for collisions, log those collisions to move the circles out of eachother later, then perform the math between all of them, and THEN I update their positions so they dont intersect; also I have managed to condense it and get it to reproduce one issue, which was the wall issue, what should I do for the other issues? – Pjbomb2 Jan 17 '20 at 20:49
  • What you may need to do is check can the object be moved before you move it. You can take some short cuts (how many times have you seen some poor sucker in a game run for a half-second or so after you shot them and then teleport back to fall and die?) but the more precise the simulation requirements the more picky you have to be. – user4581301 Jan 17 '20 at 21:00

0 Answers0