We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpPrograms › Basic pool game
Page Index Toggle Pages: 1
Basic pool game (Read 2549 times)
Basic pool game
Aug 7th, 2007, 7:50pm
 

  Hey, I'm new to Processing, just being messing around with it MobileProcessing.  

I came up with the basic workings of a vector graphics pool game (no cue yet, working on that in a seperate project).  As my maths skills aren't what they used to be I've been having trouble working out how to get the balls to collide properly while in motion and was wondering if anyone can give me any help.  

I've included the source code below if anyone wants to mess around with it (not if this should be in the Exhibition area then), it's all vector graphics so no external files are needed. the enter key creates the cue ball and the arrow keys send it an impulse in the direction pressed.

(It wont all fit in 1 post, so I've split it up in logical places.  Just copy + paste everything into Processing and it should be fine)
Re: Basic pool game
Reply #1 - Aug 7th, 2007, 7:51pm
 
///////////////////////////////////////////////////////////
//globals
Ball[] theBalls;
int numBalls;
int totalBalls = 17;

void setup() {  
 ellipseMode(CENTER);
size(400,600);
 theBalls = new Ball[totalBalls];
 numBalls = 0;
framerate(15);

int x=200;
 int nX;
 int y=400;
 int redFlag=0;
 
 for(int i=1;i<6;i++){
 nX=x;
 for(int n=1;n<(i+1);n++){
 color aColor=color(0,0,0);
 if (redFlag==0)
 {
 aColor = color(255,255,0);
 redFlag=1;
 }
 else
 {
 aColor = color(255,0,0);
 redFlag=0;
 }
 theBalls[numBalls]=new Ball(nX,y,20,0,0,aColor,numBalls,1);
 nX+=20;
 numBalls++;
 }
 y+=20;
 x-=10;
 }

}  

void draw() {  
 color backDrop=color(0,255,0);
 background(backDrop);

 for (int i=0; i < numBalls; i++) {
   
   for(int j=0;j < numBalls;j++){
   
   if(theBalls[i]!=theBalls[j]){
   theBalls[i].checkBallCollision(theBalls[i],theBalls[j]);
   }
   }
   }
 for (int i=0; i < numBalls; i++){
   theBalls[i].moveMe();
   theBalls[i].drawMe();
 }
 drawCorners();
 loop();
}  



void keyPressed(){
 if (keyCode == ENTER) {
   //create a new ball
   if(numBalls+1!=totalBalls){
   color aColor = color(255,255,255);
   theBalls[numBalls] = new Ball(100,(int) random(height-20), 20,0,-10, aColor,numBalls,2);
    numBalls = (numBalls+1)%totalBalls;
   }else
   {
     theBalls[15].v.x=-20;
     theBalls[15].v.y=-20;
   }
 
 }

 if (keyCode == UP)
  {
   theBalls[15].v.y=-20;
  }
  if (keyCode == DOWN)
  {
    theBalls[15].v.y=20;
  }
  if (keyCode == LEFT)
  {
   theBalls[15].v.x=-20;
  }
  if (keyCode == RIGHT)
  {
   theBalls[15].v.x=20;
  }
 
  }

void drawCorners()
{
   int x1=0;
   int y1=0;
   
   int x2=width;
   int y2=0;
   
   int x5=0;
   int y5=height;
   
   int x6=width;
   int y6=height;
   
   
   fill(0,0,0);
   ellipse(x1,y1,50,50);
   ellipse(x2,y2,50,50);
   ellipse(x5,y5,50,50);
   ellipse(x6,y6,50,50);
}

Re: Basic pool game
Reply #2 - Aug 7th, 2007, 7:52pm
 

class Ball {
 int xPos;
 int yPos;
 int bSize;
 Vector v;
int ballID;
  color bColor;
int mass=1;
int oldX;
int oldY;
 //constructor
 Ball(int theX, int theY, int theSize, int theDX, int theDY, color theColor,int ballID,int bMass) {
   xPos = theX;
   yPos = theY;
   bSize = theSize;
   v=new Vector(theDX,theDY);
   ballID=ballID;
   bColor = theColor;
   mass=bMass;
 }

 //moves the ball
 void moveMe() {
   oldX=xPos;
   oldY=yPos;
   xPos += v.x;
   yPos += v.y;
 
 
    if(v.x<0){
   v.x+=1;}else if(v.x>0){
   v.x-=1;}else if(v.x==0){
    }
  if(v.y<0){
   v.y+=1;}else if(v.y>0){
   v.y-=1;}else if(v.y==0){
    }    
  // v.x-=0.05;
  // v.y-=0.05;
   bounceMe();
 }

 //draws the ball
 void drawMe() {
   fill(bColor);
   ellipse(xPos, yPos, bSize, bSize);
 }
 
 //checks for collisions with edge
 void bounceMe() {
   if ((xPos+(bSize*0.5) >= width) || (xPos <= 10))
    { v.x = -v.x;
    xPos=oldX;}
   if ((yPos+(bSize*0.5) >= height) || (yPos <= 10))
  { v.y = -v.y;
   yPos=oldY; }


 }

 
 void checkBallCollision(Ball ball1, Ball ball2){
 
 //Calculate distance between circles
 int xOff=ball1.xPos-ball2.xPos;
 int yOff=ball1.yPos-ball2.yPos;
 
 float a = sq(xOff);
 float b = sq(yOff);
 float c = a + b;
 float d = abs(sqrt(c));

//if greater than sum of circle radii, collision occurs
 if(d<20){
 //Find normalized vector between circles
 Vector n=new Vector((xOff),(yOff));
 n.vectorNormal(n);
 
 //find the length along n both circles move vectors are
 
 float a1 = ball1.v.DotProduct(n,ball1.v);
 float a2 = ball2.v.DotProduct(n,ball2.v);  
 
int optimizmedP=(int)(2*(a1-a2))/(ball1.mass+ball2.mass);
optimizmedP=abs(optimizmedP);


// Calculate v1, the new movement vector of circle1


Vector v1 = new Vector(n);
v1.x=v1.x+ball1.v.x;
v1.y=v1.y+ball1.v.y;
v1.x-= (optimizmedP * ball2.mass);
v1.y-= (optimizmedP * ball2.mass);

// Calculate v2, the new movement vector of circle1

Vector v2 = new Vector(n);
v2.x=v2.x+ball1.v.x;
v2.y=v2.y+ball1.v.y;
v2.x+=(optimizmedP * ball1.mass);
v2.y+=(optimizmedP * ball1.mass);


ball1.v.x=constrain((int)v1.x,-20,20);
ball1.v.y=constrain((int)v1.y,-20,20);
ball2.v.x=constrain((int)v2.x,-25,25);
ball2.v.y=constrain((int)v2.y,-25,25);


 }

 }
Re: Basic pool game
Reply #3 - Aug 7th, 2007, 7:52pm
 

class Vector
{
 float x;
 float y;
 float vectorLength;

Vector(Vector v)
{
  x=v.x;
 y=v.y;
 vectorLength=sqrt( (x*x) + (y*y));
 
}  

Vector(int vX,int vY)
{
 x=vX;
 y=vY;
 vectorLength=sqrt( (x*x) + (y*y));
 
 
}
float vectorLength(Vector v)
 {
  return( sqrt( (v.x*v.x) + (v.y*v.y))  );
 }

Vector vectorNormal(Vector v)
{
 float temp_fp = 1/vectorLength;
 print(v.x);
 v.x *= temp_fp;
 v.y *= temp_fp;
// print(v.x);
 return( v );
}

Vector AddVect( Vector v1, Vector v2 )
{
 Vector v=new Vector(0,0);
 v.x = v1.x + v2.x;
 v.y = v1.y + v2.y;
 return( v );
}
 

Vector SubVect( Vector v1, Vector v2 )
{
 Vector v=new Vector(0,0);
 v.x = v1.x - v2.x;
 v.y = v1.y - v2.y;
 return( v );
}

Vector ScaleIncrease( Vector v, int s )
{
 v.x *= s;
 v.y *= s;
 return( v );
}

Vector ScaleDecrease( Vector v, int s)
{
 v.x /= s;
 v.y /= s;
 return( v );
}


float DotProduct( Vector v1, Vector v2 )
{
 return((v1.x*v2.x) + (v1.y*v2.y));
}

}
 
}
//////////////////////////////////////////////////////
Re: Basic pool game
Reply #4 - Aug 7th, 2007, 9:51pm
 
I can see one possible problem.
You need to check if the balls "will collide" not "have collided" as you've currently doing, or two balls coudl easily end up half way inside each other before you check to see if they've collided.

Also, your balls slow down a bit oddly. I'd got for v.x*=0.95 for a smoother movement.

Your collisions are also problematic since you only bounce in compass directions, whereas it should really be a completely variable direction.

One option to make the collisions even more accurate would be to do more than one move per drawn frame (e.g. move a tenth of the distance, 10 times, and check for collisions each small bit of movement)
Re: Basic pool game
Reply #5 - Aug 8th, 2007, 1:23am
 
The 10x / frame trick works fairly well, though it assumes you have some processing power to burn.  The other nice thing about that is that you can get blur trails real easy by drawing the earlier steps with some alpha (and perhaps draw them somewhat smaller).  I used this trick in http://www.slimearena.com/hackeysack.php (which is in Flash, not Processing, but same idea), and the ball ends up looking pretty nice, and the collisions come out well.

If you don't have enough free time in the loop to do 10x (or 5, or something like that), you can do a quick check to see if two circles could possibly collide during a time step using a simple bounding rectangle test (the rect for each would be the bounding rect for both this and the last time step).  If they can collide, you subdivide the time step and continue subdividing until you a) see that they actually don't collide, or b) find the actual collision point to within a certain tolerance, at which point you resolve it normally.

Alternatively, if you don't want to subdivide at all, you can do so-called swept collision detection.  The way this works is that you start with the positions of the two circles in the previous and current steps.  You then find the position of circle B w.r.t. circle A at each time step (basically you move into the reference frame of circle A), then you solve for the amount of time it takes for circle B to be exactly rA+rB distance from circle A.  This gives you the TOI (time of impact), which tells you exactly when they collide, and you move the particles to there, alter the velocities, then finish up the time step by moving them with the new velocities for the rest of the step.

Clearly all of the "correct" methods are a bit tricky to do, so if you can achieve the same performance just by using multiple time steps, do it - trust me, it will save some headaches.  And in reality, even the methods I outlined above don't completely solve the problem because you could be missing other collisions in the meantime.  There are ways to handle even this problem, but they get nasty and if you're not careful they can get you in situations where your single frame time step gets into an infinite loop.  At some point if I get a chance I'm planning to put together a collision detection/resolution tutorial or library using Processing, but at the moment I don't have this stuff coded up in a fashion that would be useful.

One other tip: as much as we all might like to, it's not a good idea to make a class called Vector, because Java already has a Vector object that has nothing at all to do with real geometric vectors (FWIW, whoever started this trend - I think it began with the C++ STL - should be beaten, because they wasted a really useful name on something that completely craps upon the standard usage of the word vector).  It probably won't cause problems for you here, but if you plan on using your code elsewhere, at some point you may find yourself in need of the actual Java Vector class, which will make things a bit difficult (though you should be using ArrayList instead of Vector unless you need thread safety).  Long story short: call it Vector2d instead, or something like that.

Also, a performance tip: watch out with vector methods that return new vectors.  It will work fine when you're only calling these a few hundred times per frame, but in a recent project I was using a lot of vector methods (~30,000 per frame), and I discovered that the overhead of creating a new object was taking up a good proportion of the processing power.  I was able to fix it by merely reusing the same temporary vectors over and over again whenever I needed a something that didn't persist from frame to frame.  Just keep it in mind as a potential optimization point if you use this stuff in other projects.
Re: Basic pool game
Reply #6 - Aug 8th, 2007, 5:01pm
 
I did a basic pool sketch a few months ago. It is availible (with source) at http://www.skanaar.com/applets/billiards/.

The source code might help you, but the code is very poorly commented.
Re: Basic pool game
Reply #7 - Apr 27th, 2010, 8:42am
 
@skanaar : your link is considered like a trojan
Page Index Toggle Pages: 1