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();
}