|
|
|
@ -13,6 +13,7 @@ struct Ball{ |
|
|
|
|
float radius=0; |
|
|
|
|
float mass=0; |
|
|
|
|
float simTimeRemaining=0; |
|
|
|
|
bool grounded=false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct Line{ |
|
|
|
@ -29,16 +30,19 @@ class MountainClimber : public olc::PixelGameEngine |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
std::vector<Ball>balls; |
|
|
|
|
std::vector<Ball*>balls; |
|
|
|
|
std::vector<Line>lines; |
|
|
|
|
Ball*selectedBall; |
|
|
|
|
Line*selectedLine; |
|
|
|
|
std::vector<std::pair<Ball*,Ball*>>collidingPairs; |
|
|
|
|
std::vector<Ball*>fakeBalls; |
|
|
|
|
bool selectedLineStart=false; |
|
|
|
|
float friction=0.9f; |
|
|
|
|
float gravity=200.f; |
|
|
|
|
float limit=500.f; |
|
|
|
|
float friction=5.f; |
|
|
|
|
float gravity=2000.f; |
|
|
|
|
float limit=1000.f; |
|
|
|
|
Ball*player; |
|
|
|
|
float moveSpd=250; |
|
|
|
|
float jumpSpd=1000; |
|
|
|
|
|
|
|
|
|
MountainClimber() |
|
|
|
|
{ |
|
|
|
@ -46,11 +50,10 @@ public: |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AddBall(vf2d pos,float r){ |
|
|
|
|
Ball b; |
|
|
|
|
b.pos=pos; |
|
|
|
|
b.radius=r; |
|
|
|
|
b.mass=r*10; |
|
|
|
|
b.vel.y=rand()%1000-17000; |
|
|
|
|
Ball*b=new Ball(); |
|
|
|
|
b->pos=pos; |
|
|
|
|
b->radius=r; |
|
|
|
|
b->mass=r*10; |
|
|
|
|
balls.emplace_back(b); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -67,6 +70,12 @@ public: |
|
|
|
|
public: |
|
|
|
|
bool OnUserCreate() override |
|
|
|
|
{ |
|
|
|
|
AddLine({0,360},{640,360},0.1,0); |
|
|
|
|
AddLine({340,360},{640,140},0.1,0); |
|
|
|
|
AddBall({320,300},8); |
|
|
|
|
|
|
|
|
|
ConsoleCaptureStdOut(true); |
|
|
|
|
player=balls[0]; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -79,83 +88,91 @@ public: |
|
|
|
|
return fabs((pos1.x - pos2.x)*(pos1.x - pos2.x) + (pos1.y - pos2.y)*(pos1.y - pos2.y)) <= (r1 + r2)*(r1 + r2); |
|
|
|
|
}; |
|
|
|
|
for(int i=0;i<simulationUpdates;i++){ |
|
|
|
|
for(Ball&b:balls){ |
|
|
|
|
b.simTimeRemaining=simElapsedTime; |
|
|
|
|
for(Ball*b:balls){ |
|
|
|
|
b->simTimeRemaining=simElapsedTime; |
|
|
|
|
} |
|
|
|
|
for(int j=0;j<maxSimulationSteps;j++){ |
|
|
|
|
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*friction; |
|
|
|
|
b.acc.y=-b.vel.y*friction+gravity; //Gravity constant;
|
|
|
|
|
b.vel.x+=b.acc.x*b.simTimeRemaining; |
|
|
|
|
b.vel.y+=b.acc.y*b.simTimeRemaining; |
|
|
|
|
b.pos.x+=b.vel.x*b.simTimeRemaining; |
|
|
|
|
b.pos.y+=b.vel.y*b.simTimeRemaining; |
|
|
|
|
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*friction; |
|
|
|
|
b->acc.y=-b->vel.y*friction+((!b->grounded)?gravity:0); //Gravity constant;
|
|
|
|
|
b->vel.x+=b->acc.x*b->simTimeRemaining; |
|
|
|
|
b->vel.y+=b->acc.y*b->simTimeRemaining; |
|
|
|
|
b->pos.x+=b->vel.x*b->simTimeRemaining; |
|
|
|
|
b->pos.y+=b->vel.y*b->simTimeRemaining; |
|
|
|
|
|
|
|
|
|
if(b.pos.x<0){ |
|
|
|
|
b.pos.x+=ScreenWidth(); |
|
|
|
|
if(b->pos.x<0){ |
|
|
|
|
b->pos.x+=ScreenWidth(); |
|
|
|
|
} |
|
|
|
|
if(b.pos.y<0){ |
|
|
|
|
b.pos.y+=ScreenHeight(); |
|
|
|
|
if(b->pos.y<0){ |
|
|
|
|
b->pos.y+=ScreenHeight(); |
|
|
|
|
} |
|
|
|
|
if(b.pos.x>=ScreenWidth()){ |
|
|
|
|
b.pos.x-=ScreenWidth(); |
|
|
|
|
if(b->pos.x>=ScreenWidth()){ |
|
|
|
|
b->pos.x-=ScreenWidth(); |
|
|
|
|
} |
|
|
|
|
if(b.pos.y>=ScreenHeight()){ |
|
|
|
|
b.pos.y-=ScreenHeight(); |
|
|
|
|
if(b->pos.y>=ScreenHeight()){ |
|
|
|
|
b->pos.y-=ScreenHeight(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(fabs(b.vel.x*b.vel.x+b.vel.y*b.vel.y)<stable){ |
|
|
|
|
b.vel.x=0; |
|
|
|
|
b.vel.y=0; |
|
|
|
|
if(fabs(b->vel.x*b->vel.x+b->vel.y*b->vel.y)<stable){ |
|
|
|
|
b->vel.x=0; |
|
|
|
|
b->vel.y=0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for(Ball&b:balls){ |
|
|
|
|
float deltaTime=b.simTimeRemaining; |
|
|
|
|
for(Ball*b:balls){ |
|
|
|
|
float deltaTime=b->simTimeRemaining; |
|
|
|
|
bool collisionOccured=false; |
|
|
|
|
for(Line&l:lines){ |
|
|
|
|
if(!l.oneWay||l.oneWay&&b.vel.y<0){ |
|
|
|
|
if((!l.oneWay||l.oneWay&&b->vel.y<0)){ |
|
|
|
|
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}; |
|
|
|
|
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.y}; |
|
|
|
|
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){ |
|
|
|
|
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*l.bounceFactor; |
|
|
|
|
fakeBall->mass=b->mass*l.bounceFactor; |
|
|
|
|
fakeBall->pos={closestPoint.x,closestPoint.y}; |
|
|
|
|
fakeBall->vel={std::min(limit,std::max(-limit,-b.vel.x)),std::min(limit,std::max(-limit,-b.vel.y))}; |
|
|
|
|
fakeBall->vel={std::min(limit,std::max(-limit,-b->vel.x)),std::min(limit,std::max(-limit,-b->vel.y))}; |
|
|
|
|
fakeBalls.push_back(fakeBall); |
|
|
|
|
collidingPairs.push_back({&b,fakeBall}); |
|
|
|
|
float overlap=1.1f*(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; |
|
|
|
|
collidingPairs.push_back({b,fakeBall}); |
|
|
|
|
float overlap=1.1f*(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; |
|
|
|
|
if(b==player&&fakeBall->vel.y<0){//This is an upwards force pushing on us, we have touched ground.
|
|
|
|
|
b->grounded=true; |
|
|
|
|
collisionOccured=true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for(Ball&b2:balls){ |
|
|
|
|
if(&b!=&b2){ |
|
|
|
|
if(DoCirclesOverlap(b.pos,b.radius,b2.pos,b2.radius)){ |
|
|
|
|
collidingPairs.push_back({&b,&b2}); |
|
|
|
|
float dist=sqrtf((b.pos.x-b2.pos.x)*(b.pos.x-b2.pos.x)+(b.pos.y-b2.pos.y)*(b.pos.y-b2.pos.y)); |
|
|
|
|
float overlap=0.5f*(dist-b.radius-b2.radius); |
|
|
|
|
b.pos.x-=overlap*(b.pos.x-b2.pos.x)/dist; |
|
|
|
|
b.pos.y-=overlap*(b.pos.y-b2.pos.y)/dist; |
|
|
|
|
b2.pos.x+=overlap*(b.pos.x-b2.pos.x)/dist; |
|
|
|
|
b2.pos.y+=overlap*(b.pos.y-b2.pos.y)/dist; |
|
|
|
|
if(!collisionOccured){ |
|
|
|
|
b->grounded=false; |
|
|
|
|
} |
|
|
|
|
for(Ball*b2:balls){ |
|
|
|
|
if(b!=b2){ |
|
|
|
|
if(DoCirclesOverlap(b->pos,b->radius,b2->pos,b2->radius)){ |
|
|
|
|
collidingPairs.push_back({b,b2}); |
|
|
|
|
float dist=sqrtf((b->pos.x-b2->pos.x)*(b->pos.x-b2->pos.x)+(b->pos.y-b2->pos.y)*(b->pos.y-b2->pos.y)); |
|
|
|
|
float overlap=0.5f*(dist-b->radius-b2->radius); |
|
|
|
|
b->pos.x-=overlap*(b->pos.x-b2->pos.x)/dist; |
|
|
|
|
b->pos.y-=overlap*(b->pos.y-b2->pos.y)/dist; |
|
|
|
|
b2->pos.x+=overlap*(b->pos.x-b2->pos.x)/dist; |
|
|
|
|
b2->pos.y+=overlap*(b->pos.y-b2->pos.y)/dist; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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 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; |
|
|
|
|
b->simTimeRemaining=b->simTimeRemaining-actualTime; |
|
|
|
|
} |
|
|
|
|
float efficiency=1; |
|
|
|
|
for(std::pair<Ball*,Ball*>&pair:collidingPairs){ |
|
|
|
@ -188,12 +205,25 @@ public: |
|
|
|
|
return fabs((pos.x - point.x)*(pos.x - point.x) + (pos.y - point.y)*(pos.y - point.y)) < (r * r); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if(GetKey(A).bHeld){ |
|
|
|
|
player->vel.x=-moveSpd; |
|
|
|
|
player->grounded=false; |
|
|
|
|
} |
|
|
|
|
if(GetKey(D).bHeld){ |
|
|
|
|
player->vel.x=moveSpd; |
|
|
|
|
player->grounded=false; |
|
|
|
|
} |
|
|
|
|
if(GetKey(SPACE).bPressed){ |
|
|
|
|
player->vel.y=-jumpSpd; |
|
|
|
|
player->grounded=false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (GetMouse(0).bPressed){ |
|
|
|
|
selectedBall = nullptr; |
|
|
|
|
for (Ball&b:balls){ |
|
|
|
|
if (IsPointInCircle(b.pos,b.radius,GetMousePos())) |
|
|
|
|
for (Ball*b:balls){ |
|
|
|
|
if (IsPointInCircle(b->pos,b->radius,GetMousePos())) |
|
|
|
|
{ |
|
|
|
|
selectedBall = &b; |
|
|
|
|
selectedBall = b; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -233,9 +263,9 @@ public: |
|
|
|
|
selectedLine=nullptr; |
|
|
|
|
} |
|
|
|
|
if(GetMouse(1).bHeld){ |
|
|
|
|
for(Ball&b:balls){ |
|
|
|
|
b.vel.x+=(GetMouseX()-b.pos.x)*0.01f; |
|
|
|
|
b.vel.y+=(GetMouseY()-b.pos.y)*0.01f; |
|
|
|
|
for(Ball*b:balls){ |
|
|
|
|
b->vel.x+=(GetMouseX()-b->pos.x)*0.01f; |
|
|
|
|
b->vel.y+=(GetMouseY()-b->pos.y)*0.01f; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -245,17 +275,13 @@ public: |
|
|
|
|
|
|
|
|
|
Clear(BLACK); |
|
|
|
|
|
|
|
|
|
if(GetKey(SPACE).bPressed){ |
|
|
|
|
AddBall({610,620},rand()%4+8); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
HandlePhysics(fElapsedTime); |
|
|
|
|
|
|
|
|
|
for(Ball&b:balls){ |
|
|
|
|
if(selectedBall==&b){ |
|
|
|
|
FillCircle(b.pos,b.radius,YELLOW); |
|
|
|
|
for(Ball*b:balls){ |
|
|
|
|
if(selectedBall==b){ |
|
|
|
|
FillCircle(b->pos,b->radius,YELLOW); |
|
|
|
|
} else { |
|
|
|
|
FillCircle(b.pos,b.radius); |
|
|
|
|
FillCircle(b->pos,b->radius); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for(Line&l:lines){ |
|
|
|
|