|
|
@ -9,9 +9,16 @@ struct Ball{ |
|
|
|
vf2d pos={}; |
|
|
|
vf2d pos={}; |
|
|
|
vf2d vel={}; |
|
|
|
vf2d vel={}; |
|
|
|
vf2d acc={}; |
|
|
|
vf2d acc={}; |
|
|
|
|
|
|
|
vf2d originalPos={}; |
|
|
|
float radius=0; |
|
|
|
float radius=0; |
|
|
|
float mass=0; |
|
|
|
float mass=0; |
|
|
|
|
|
|
|
float simTimeRemaining=0; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Line{ |
|
|
|
|
|
|
|
vf2d startPos={}; |
|
|
|
|
|
|
|
vf2d endPos={}; |
|
|
|
|
|
|
|
float radius=0; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class MiniGolfPro : public olc::PixelGameEngine |
|
|
|
class MiniGolfPro : public olc::PixelGameEngine |
|
|
@ -19,8 +26,11 @@ class MiniGolfPro : public olc::PixelGameEngine |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
public: |
|
|
|
std::vector<Ball>balls; |
|
|
|
std::vector<Ball>balls; |
|
|
|
|
|
|
|
std::vector<Line>lines; |
|
|
|
Ball*selectedBall; |
|
|
|
Ball*selectedBall; |
|
|
|
|
|
|
|
Line*selectedLine; |
|
|
|
std::vector<std::pair<Ball*,Ball*>>collidingPairs; |
|
|
|
std::vector<std::pair<Ball*,Ball*>>collidingPairs; |
|
|
|
|
|
|
|
std::vector<Ball*>fakeBalls; |
|
|
|
|
|
|
|
|
|
|
|
MiniGolfPro() |
|
|
|
MiniGolfPro() |
|
|
|
{ |
|
|
|
{ |
|
|
@ -35,12 +45,26 @@ public: |
|
|
|
balls.emplace_back(b); |
|
|
|
balls.emplace_back(b); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AddLine(vf2d pos1,vf2d pos2,float r){ |
|
|
|
|
|
|
|
Line l; |
|
|
|
|
|
|
|
l.startPos=pos1; |
|
|
|
|
|
|
|
l.endPos=pos2; |
|
|
|
|
|
|
|
l.radius=r; |
|
|
|
|
|
|
|
lines.push_back(l); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
public: |
|
|
|
bool OnUserCreate() override |
|
|
|
bool OnUserCreate() override |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (int i=0;i<20;i++){ |
|
|
|
for (int i=0;i<20;i++){ |
|
|
|
AddBall({float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},rand()%16+2); |
|
|
|
AddBall({float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},rand()%16+2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
AddLine({12,4},{64,4},5); |
|
|
|
|
|
|
|
AddLine({76,4},{132,4},5); |
|
|
|
|
|
|
|
AddLine({12,68},{64,68},5); |
|
|
|
|
|
|
|
AddLine({76,68},{132,68},5); |
|
|
|
|
|
|
|
AddLine({4,12},{4,60},5); |
|
|
|
|
|
|
|
AddLine({140,12},{140,60},5); |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -83,13 +107,27 @@ public: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Clear(BLACK); |
|
|
|
Clear(BLACK); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float stable=0.01f; |
|
|
|
|
|
|
|
int simulationUpdates=4; |
|
|
|
|
|
|
|
int maxSimulationSteps=15; |
|
|
|
|
|
|
|
float simElapsedTime=fElapsedTime/(float)simulationUpdates; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(int i=0;i<simulationUpdates;i++){ |
|
|
|
|
|
|
|
for(Ball&b:balls){ |
|
|
|
|
|
|
|
b.simTimeRemaining=simElapsedTime; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for(int j=0;j<maxSimulationSteps;j++){ |
|
|
|
for(Ball&b:balls){ |
|
|
|
for(Ball&b:balls){ |
|
|
|
|
|
|
|
if(b.simTimeRemaining>0){ |
|
|
|
|
|
|
|
b.originalPos.x=b.pos.x; |
|
|
|
|
|
|
|
b.originalPos.y=b.pos.y; |
|
|
|
b.acc.x=-b.vel.x*0.8f; |
|
|
|
b.acc.x=-b.vel.x*0.8f; |
|
|
|
b.acc.y=-b.vel.y*0.8f; |
|
|
|
b.acc.y=-b.vel.y*0.8f/*+100.f //Gravity constant*/; |
|
|
|
b.vel.x+=b.acc.x*fElapsedTime; |
|
|
|
b.vel.x+=b.acc.x*b.simTimeRemaining; |
|
|
|
b.vel.y+=b.acc.y*fElapsedTime; |
|
|
|
b.vel.y+=b.acc.y*b.simTimeRemaining; |
|
|
|
b.pos.x+=b.vel.x*fElapsedTime; |
|
|
|
b.pos.x+=b.vel.x*b.simTimeRemaining; |
|
|
|
b.pos.y+=b.vel.y*fElapsedTime; |
|
|
|
b.pos.y+=b.vel.y*b.simTimeRemaining; |
|
|
|
|
|
|
|
|
|
|
|
if(b.pos.x<0){ |
|
|
|
if(b.pos.x<0){ |
|
|
|
b.pos.x+=ScreenWidth(); |
|
|
|
b.pos.x+=ScreenWidth(); |
|
|
@ -104,13 +142,35 @@ public: |
|
|
|
b.pos.y-=ScreenHeight(); |
|
|
|
b.pos.y-=ScreenHeight(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(fabs(b.vel.x*b.vel.x+b.vel.y*b.vel.y)<0.01){ |
|
|
|
if(fabs(b.vel.x*b.vel.x+b.vel.y*b.vel.y)<stable){ |
|
|
|
b.vel.x=0; |
|
|
|
b.vel.x=0; |
|
|
|
b.vel.y=0; |
|
|
|
b.vel.y=0; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for(Ball&b:balls){ |
|
|
|
for(Ball&b:balls){ |
|
|
|
|
|
|
|
float deltaTime=b.simTimeRemaining; |
|
|
|
|
|
|
|
for(Line&l:lines){ |
|
|
|
|
|
|
|
vf2d line1={l.endPos.x-l.startPos.x,l.endPos.y-l.startPos.y}; |
|
|
|
|
|
|
|
vf2d line2={b.pos.x-l.startPos.x,b.pos.y-l.startPos.y}; |
|
|
|
|
|
|
|
float edgeLength=line1.x*line1.x+line1.y*line1.y; |
|
|
|
|
|
|
|
float t=std::max(0.f,std::min(edgeLength,(line1.x*line2.x+line1.y*line2.y)))/edgeLength; |
|
|
|
|
|
|
|
vf2d closestPoint={l.startPos.x+t*line1.x,l.startPos.y+t*line1.x}; |
|
|
|
|
|
|
|
float dist=sqrtf((b.pos.x-closestPoint.x)*(b.pos.x-closestPoint.x)+(b.pos.y-closestPoint.y)*(b.pos.y-closestPoint.y)); |
|
|
|
|
|
|
|
if(dist<=b.radius+l.radius){ |
|
|
|
|
|
|
|
Ball*fakeBall=new Ball(); |
|
|
|
|
|
|
|
fakeBall->radius=l.radius; |
|
|
|
|
|
|
|
fakeBall->mass=b.mass*0.8f; |
|
|
|
|
|
|
|
fakeBall->pos=closestPoint; |
|
|
|
|
|
|
|
fakeBall->vel={-b.vel.x,-b.vel.y}; |
|
|
|
|
|
|
|
fakeBalls.push_back(fakeBall); |
|
|
|
|
|
|
|
collidingPairs.push_back({&b,fakeBall}); |
|
|
|
|
|
|
|
float overlap=1.f*(dist-b.radius-fakeBall->radius); |
|
|
|
|
|
|
|
b.pos.x-=overlap*(b.pos.x-fakeBall->pos.x)/dist; |
|
|
|
|
|
|
|
b.pos.y-=overlap*(b.pos.y-fakeBall->pos.y)/dist; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
for(Ball&b2:balls){ |
|
|
|
for(Ball&b2:balls){ |
|
|
|
if(&b!=&b2){ |
|
|
|
if(&b!=&b2){ |
|
|
|
if(DoCirclesOverlap(b.pos,b.radius,b2.pos,b2.radius)){ |
|
|
|
if(DoCirclesOverlap(b.pos,b.radius,b2.pos,b2.radius)){ |
|
|
@ -124,8 +184,13 @@ public: |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
float intendedSpeed=sqrtf(b.vel.x+b.vel.y*b.vel.y); |
|
|
|
|
|
|
|
float intendedDist=intendedSpeed*b.simTimeRemaining; |
|
|
|
|
|
|
|
float actualDist=sqrtf((b.pos.x-b.originalPos.x)*(b.pos.x-b.originalPos.x)+(b.pos.y-b.originalPos.y)*(b.pos.y-b.originalPos.y)); |
|
|
|
|
|
|
|
float actualTime=actualDist/intendedSpeed; |
|
|
|
|
|
|
|
b.simTimeRemaining=b.simTimeRemaining-actualTime; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
float efficiency=1; |
|
|
|
for(std::pair<Ball*,Ball*>&pair:collidingPairs){ |
|
|
|
for(std::pair<Ball*,Ball*>&pair:collidingPairs){ |
|
|
|
Ball*b1=pair.first; |
|
|
|
Ball*b1=pair.first; |
|
|
|
Ball*b2=pair.second; |
|
|
|
Ball*b2=pair.second; |
|
|
@ -142,6 +207,12 @@ public: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
collidingPairs.clear(); |
|
|
|
collidingPairs.clear(); |
|
|
|
|
|
|
|
for(Ball*fb:fakeBalls){ |
|
|
|
|
|
|
|
delete fb; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fakeBalls.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for(Ball&b:balls){ |
|
|
|
for(Ball&b:balls){ |
|
|
|
if(selectedBall==&b){ |
|
|
|
if(selectedBall==&b){ |
|
|
@ -150,6 +221,15 @@ public: |
|
|
|
FillCircle(b.pos,b.radius); |
|
|
|
FillCircle(b.pos,b.radius); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for(Line&l:lines){ |
|
|
|
|
|
|
|
FillCircle(l.startPos,l.radius); |
|
|
|
|
|
|
|
FillCircle(l.endPos,l.radius); |
|
|
|
|
|
|
|
vf2d normal={-(l.endPos.y-l.startPos.y),l.endPos.x-l.startPos.x}; |
|
|
|
|
|
|
|
float dist=sqrt(normal.x*normal.x+normal.y*normal.y); |
|
|
|
|
|
|
|
normal/=dist; |
|
|
|
|
|
|
|
DrawLine(l.startPos.x+normal.x*l.radius,l.startPos.y+normal.y*l.radius,l.endPos.x+normal.x*l.radius,l.endPos.y+normal.y*l.radius); |
|
|
|
|
|
|
|
DrawLine(l.startPos.x-normal.x*l.radius,l.startPos.y-normal.y*l.radius,l.endPos.x-normal.x*l.radius,l.endPos.y-normal.y*l.radius); |
|
|
|
|
|
|
|
} |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|