|
|
|
#include "pixelGameEngine.h"
|
|
|
|
|
|
|
|
using namespace olc;
|
|
|
|
|
|
|
|
#define WIDTH 640
|
|
|
|
#define HEIGHT 480
|
|
|
|
|
|
|
|
struct Ball{
|
|
|
|
vf2d pos={};
|
|
|
|
vf2d vel={};
|
|
|
|
vf2d acc={};
|
|
|
|
float radius=0;
|
|
|
|
float mass=0;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class MiniGolfPro : public olc::PixelGameEngine
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::vector<Ball>balls;
|
|
|
|
Ball*selectedBall;
|
|
|
|
std::vector<std::pair<Ball*,Ball*>>collidingPairs;
|
|
|
|
|
|
|
|
MiniGolfPro()
|
|
|
|
{
|
|
|
|
sAppName = "Mini Golf Pro";
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddBall(vf2d pos,float r){
|
|
|
|
Ball b;
|
|
|
|
b.pos=pos;
|
|
|
|
b.radius=r;
|
|
|
|
b.mass=r*10;
|
|
|
|
balls.emplace_back(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool OnUserCreate() override
|
|
|
|
{
|
|
|
|
for (int i=0;i<20;i++){
|
|
|
|
AddBall({float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},rand()%16+2);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnUserUpdate(float fElapsedTime) override
|
|
|
|
{
|
|
|
|
auto DoCirclesOverlap = [](vf2d pos1, float r1, vf2d pos2, float r2){
|
|
|
|
return fabs((pos1.x - pos2.x)*(pos1.x - pos2.x) + (pos1.y - pos2.y)*(pos1.y - pos2.y)) <= (r1 + r2)*(r1 + r2);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto IsPointInCircle = [](vf2d pos, float r, vf2d point){
|
|
|
|
return fabs((pos.x - point.x)*(pos.x - point.x) + (pos.y - point.y)*(pos.y - point.y)) < (r * r);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (GetMouse(0).bPressed||GetMouse(1).bPressed){
|
|
|
|
selectedBall = nullptr;
|
|
|
|
for (Ball&b:balls){
|
|
|
|
if (IsPointInCircle(b.pos,b.radius,GetMousePos()))
|
|
|
|
{
|
|
|
|
selectedBall = &b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(GetMouse(0).bHeld){
|
|
|
|
if(selectedBall!=nullptr){
|
|
|
|
selectedBall->pos.x=GetMouseX();
|
|
|
|
selectedBall->pos.y=GetMouseY();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(GetMouse(0).bReleased){
|
|
|
|
selectedBall=nullptr;
|
|
|
|
}
|
|
|
|
if(GetMouse(1).bReleased){
|
|
|
|
if(selectedBall!=nullptr){
|
|
|
|
selectedBall->vel.x=5*(selectedBall->pos.x-(float)GetMouseX());
|
|
|
|
selectedBall->vel.y=5*(selectedBall->pos.y-(float)GetMouseY());
|
|
|
|
}
|
|
|
|
selectedBall=nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Clear(BLACK);
|
|
|
|
for(Ball&b:balls){
|
|
|
|
b.acc.x=-b.vel.x*0.8f;
|
|
|
|
b.acc.y=-b.vel.y*0.8f;
|
|
|
|
b.vel.x+=b.acc.x*fElapsedTime;
|
|
|
|
b.vel.y+=b.acc.y*fElapsedTime;
|
|
|
|
b.pos.x+=b.vel.x*fElapsedTime;
|
|
|
|
b.pos.y+=b.vel.y*fElapsedTime;
|
|
|
|
|
|
|
|
if(b.pos.x<0){
|
|
|
|
b.pos.x+=ScreenWidth();
|
|
|
|
}
|
|
|
|
if(b.pos.y<0){
|
|
|
|
b.pos.y+=ScreenHeight();
|
|
|
|
}
|
|
|
|
if(b.pos.x>=ScreenWidth()){
|
|
|
|
b.pos.x-=ScreenWidth();
|
|
|
|
}
|
|
|
|
if(b.pos.y>=ScreenHeight()){
|
|
|
|
b.pos.y-=ScreenHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(fabs(b.vel.x*b.vel.x+b.vel.y*b.vel.y)<0.01){
|
|
|
|
b.vel.x=0;
|
|
|
|
b.vel.y=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for(Ball&b:balls){
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for(std::pair<Ball*,Ball*>&pair:collidingPairs){
|
|
|
|
Ball*b1=pair.first;
|
|
|
|
Ball*b2=pair.second;
|
|
|
|
float dist=sqrtf((b1->pos.x-b2->pos.x)*(b1->pos.x-b2->pos.x)+(b1->pos.y-b2->pos.y)*(b1->pos.y-b2->pos.y));
|
|
|
|
vf2d normal={(b2->pos.x-b1->pos.x)/dist,(b2->pos.y-b1->pos.y)/dist};
|
|
|
|
vf2d tangent={-normal.y,normal.x};
|
|
|
|
vf2d dpTangent={tangent.dot(b1->vel),tangent.dot(b2->vel)};
|
|
|
|
vf2d dpNormal={normal.dot(b1->vel),normal.dot(b2->vel)};
|
|
|
|
vf2d momentum={(dpNormal.x*(b1->mass-b2->mass)+2.f*b2->mass*dpNormal.y)/(b1->mass+b2->mass),(dpNormal.y*(b2->mass-b1->mass)+2.f*b1->mass*dpNormal.x)/(b1->mass+b2->mass)};
|
|
|
|
b1->vel.x=tangent.x*dpTangent.x+normal.x*momentum.x;
|
|
|
|
b1->vel.y=tangent.y*dpTangent.x+normal.y*momentum.x;
|
|
|
|
b2->vel.x=tangent.x*dpTangent.y+normal.x*momentum.y;
|
|
|
|
b2->vel.y=tangent.y*dpTangent.y+normal.y*momentum.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
collidingPairs.clear();
|
|
|
|
|
|
|
|
for(Ball&b:balls){
|
|
|
|
if(selectedBall==&b){
|
|
|
|
FillCircle(b.pos,b.radius,YELLOW);
|
|
|
|
} else {
|
|
|
|
FillCircle(b.pos,b.radius);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnUserDestroy()override{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
MiniGolfPro demo;
|
|
|
|
if (demo.Construct(640, 480, 4, 4))
|
|
|
|
demo.Start();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|