SOS! Hit Detection help in Pong Game

Hello! I’m having a hard time getting a hit detection between my ball and paddle. I was able to get it to work, but it stopped working once I migrated my code into classes. I’ll include my code that works as well as the same code that I’m trying to make work with classes. Also, I’m trying to use a Boolean function for the hit detection.

I appreciate any feedback! Also, this is my first post, so I apologize in advance for any mistakes.

//--------------------------functioning code, but not organized in classes
float ychange = 0;
float paddleXPos = 550;
float paddleYPos = 250;
float paddleWidth = 20;
float paddleHeight = 100;

float CircX = 0;
float CircY = 300;
float CircWidth = 10;
float CircHeight = 10;
float CircSpeedX = 1;
float CircSpeedY = 2;

//makes ball play at a random speed each time
float r = random(15);
void setup() {
size (600, 600);
}
void draw() {
frameRate(30*r);
background (0);

//----------------------------------------------------------------------------//Paddle
paddleYPos += ychange;
paddleYPos = constrain(paddleYPos, paddleYPos/2, height-paddleHeight/2);
float centerRectY = constrain(mouseY, 0, height-paddleHeight);
rect(paddleXPos, centerRectY, paddleWidth, paddleHeight);
//---------------------------------------------------------------------------//Collision
//checks if Circ keeps moving in current X direction, will it collide with the Paddle
if (CircX + CircWidth > paddleXPos &&
CircX< paddleXPos + paddleWidth &&
CircY + CircHeight > centerRectY &&
CircY < centerRectY + paddleHeight) {
CircSpeedX *= -1;
}
//makes Circ bounce off left and right edges of screen
else if (CircX < 0 || CircX + CircWidth > width) {
CircSpeedX *= -1;
}

//checks if Circ keeps moving in current Y direction, will it collide with the Paddle
if (CircX + CircWidth > paddleXPos &&
CircX < paddleXPos + paddleWidth &&
CircY + CircHeight > centerRectY &&
CircY< centerRectY + paddleHeight) {
CircSpeedY = -1;
}
//bounce off top and bottom edges of screen
else if (CircY < 0 || CircY + CircHeight > height) {
CircSpeedY = -1;
}
CircX += CircSpeedX;
CircY += CircSpeedY;
ellipse(CircX, CircY, CircWidth
2, CircHeight
2);
}

//---------------------same code, but trying to organize in classes. Not working. I have the boolean for the hit detection under the Paddle class.

Paddle rightPaddle;
Ball puck;

// Global Variables
int score = 0;
float r = random(15);

void setup() {
size (600, 600);
noStroke();
// Create object and pass in parameters
rightPaddle = new Paddle();
puck = new Ball();
noCursor();
}
void draw() {
background (127);

puck.display();
rightPaddle.display();
}
class Ball {

// fields

float circX; // 0
float circY; // 300
float circHeight = 10; // Maybe make this a settable variable in the future?1
float circWidth= 10;
float circSpeedX;
float circSpeedY;

// constructor

Ball() {
startBall();
}

//makes ball play at a random speed each time

// methods

void startBall() {
circX =0;
circY = random(height);
circSpeedX = random(3, 5);
circSpeedY = random(-2, 2);
}

void display() {
ellipse(circX, circY, circWidth2, circHeight2);
circX += circSpeedX;
circY += circSpeedY;

// Edge Collision Detection 
leftRightCheck();
topBottomCheck();

}

void leftRightCheck() {
if (circX > width) {
startBall();
}

if (circX < 0) {
  reverseX();
}

}

void topBottomCheck() {
// TOP
if (circY < 0 + circHeight) {
reverseY();
}

// Bottom
if (circY > height - circHeight) {
  reverseY();
}

}

void reverseX() {
circSpeedX *= -1;
}

void reverseY() {
circSpeedY *= -1;
}
}
class Paddle {

float yChange = 0;
float pXPos = 550;
float pYPos = 250;
float pWidth = 20;
float pHeight = 100;
float constrainPY;

Paddle() {
}

void display() {
pYPos += yChange;
pYPos = constrain(pYPos, pYPos/2, height-pHeight/2);
float constrainPY = constrain(mouseY, 0, height-pHeight);
rect(pXPos, constrainPY, pWidth, pHeight);

if (hitPaddle(pXPos, pYPos, pWidth, pHeight, constrainPY, puck.circX, puck.circY, puck.circHeight, puck.circWidth, puck.circSpeedX, 
  puck.circSpeedY)) {
}

}

boolean hitPaddle(float paddleX, float paddleY, float paddleWidth, float paddleHeight,
float constrainPY, float ballX, float ballY, float circWidth, float circHeight,
float circSpeedX, float circSpeedY) {

if (ballX +circWidth> paddleX && 
  ballX< paddleX + paddleWidth && 
  ballY + circHeight > constrainPY && 
  ballY< constrainPY + paddleHeight) {
  circSpeedX *= -1;
}

if (ballX+ circWidth > paddleX && 
  ballX < paddleX + paddleWidth && 
  ballY+ circHeight > constrainPY && 
  ballY< constrainPY + paddleHeight) {
  circSpeedY *= -1;

  return true;
}
return false;

}
}

int Tx=100;
int Ty=150;
int Oy=300;
int Ox=300;
int Fx=250;
int Fy=300;

void setup(){
  size(600,600);
  background(51);
  //koordináta rendszere, a függőleges helyett görbe kellene
}

void draw() {
  if (mousePressed &&Ty!=0 ) {
    Tx=mouseX;
    Ty=mouseY;
    background(51);
  } 
 
  if (mousePressed && Ty>Oy-10 && Ty<Oy+10  ) {
    Fx=mouseX;
     Tx=100;
Ty=150;
    background(51);
  } 
stroke(255,255,255); 
strokeWeight(1);
line(0,Oy,600,Oy);
line(300,0,300,600);
strokeWeight(1);
noFill();
arc(Ox-((Ox-Fx)*4),Oy,(Ox-Fx)*8,(Ox-Fx)*8,-QUARTER_PI,QUARTER_PI);

  
  //tárgy
strokeWeight(3);
stroke(249,15,15);
line(Tx,Oy,Tx,Ty); // object height and distance from origin


//tárgy teteje vonal
stroke(51,255,0);
strokeWeight(2);
line(Tx,Ty,Ox,Ty); //vízszint
line(Ox,Ty,Fx,Fy); // fókuszpont??? számolás???
line(Fx,Fy,Ox-(2*(Ox-Fx)),600-Ty);

//Középpontba menő sugár
stroke(0,149,209);
line(Tx,Ty,Ox,Oy); // from object to origin
line(Ox,Oy,Tx,600-Ty); //tükröződik


//kép
stroke(249,160,15);
float f=Ox-Fx;//50
float x=Ox-Tx;//200
float y=Oy-Ty;//300-150=150
float Kx=(f*x)/(x-f);//50*200/(200-50)=10000/150

float m=Kx/x;
float Ky=m*y;

line(Ox-Kx,Oy,Ox-Kx,Oy+Ky); // where image is formed

//fókuszpont
stroke(0);
fill(0);
ellipse(Fx,Fy,5,5); //fókuszpont
ellipse(Ox-((Ox-Fx)*2),Oy,5,5);// gyújtó pont?

}
1 Like

Nagyon szépen köszönöm redzoll85:nerd_face: ! Szeretem ezt. A példakód nagyon hasznos volt, és minden bizonnyal hasznos lesz a jövőbeni projektek számára. Meg tudtam kitalálni a találatot. Itt van, amit én jöttem fel.

Ez egy ilyen egyszerű játék, de sokkal hosszabb időt vett igénybe, mint amire számítottam!

Nagyon köszönöm a hozzászólásodat!

Susana

//Pong sketch organized with classes

// Declare object
Paddle rightPaddle;
Ball puck;

// Global Variables
int score = 0;

void setup() {
size (600, 600);
noStroke();
// Create object and pass in parameters
rightPaddle = new Paddle();
puck = new Ball();

noCursor();
}

void draw() {
background (127);

puck.display();
rightPaddle.display();

if (hitPaddle(rightPaddle.pHeight, puck.circX, puck.circY, puck.circDiameter, puck.circSpeedX)) {
puck.reverseX();
}
}

boolean hitPaddle(float paddleHeight, float ballX, float ballY, float ballDiameter, float circSpeedX) {

//Ez a rész a kódomban, amivel küzdöttem, de képes voltam dolgozni. Még egyszer köszönöm a //segítségét!
if (ballX+ballDiameter/2>width-39&&ballY>mouseY-paddleHeight/2&&ballY<mouseY+paddleHeight/2) {
circSpeedX *= -1;

print("BOUNCE");
return true;

}

return false;
}

class Ball {
// fields
float circX; // 0
float circY; // 300
float circDiameter = 20;
float circSpeedX;
float circSpeedY;
int xDirection = 1;
int yDirection = 1;

// constructor

Ball() {
startBall();
}

//makes ball play at a random speed each time

// methods

void startBall() {
circX = 40;
circY = random(height);
circSpeedX = random(3, 5);
circSpeedY = random(-2, 2);
}

void display() {
ellipse(circX, circY, circDiameter, circDiameter);
circX += circSpeedX;
circY += circSpeedY;

// Edge Collision Detection 
leftRightCheck();
topBottomCheck();

}

void leftRightCheck() {
if (circX > width) {
startBall();
}

if (circX < 0) {
  reverseX();
}

}

void topBottomCheck() {
// TOP
if (circY < 0 + circDiameter/2) {
reverseY();
}

// Bottom
if (circY > height - circDiameter/2) {
  reverseY();
}

}

void reverseX() {
circSpeedX *= -1;
}

void reverseY() {
circSpeedY *= -1;
}
}
class Paddle {

float yChange = 0;
float pXPos = 560;
float pYPos = 250;
float pWidth = 20;
float pHeight = 100;
float constrainPY;

// Set initial values in constructor

Paddle() {
}
// Fix problem on the top of the screen so that the paddle goes all the way up.
void display() {
pYPos += yChange;
float constrainPY = constrain(mouseY, 0, height);
rect(pXPos, constrainPY, pWidth, pHeight);
}
}

There are several problems.

e.g. above in hitPaddle you assume that the mouse is in the center of the paddle.

But the mouse is at the top of the paddle.

To let the paddle really be around the mouse, say rectMode(CENTER); in setup().

Bounce

In general, don’t use *-1 to bounce.

Because when the ball is still in the bounce zone of the screen border and you say *-1 twice, the ball stutters, it’s speed is positive and negative and then positive again.

Instead check upper and lower border separately and use abs() to cut off the minus sign, then it’s always a positive value. That’s good for the left border. Even when we are many times in the bounce zone the speed is always positive. For the right side say circSpeedX = abs(circSpeedX) * -1; // always negative.
That’s always negative, no matter if we bounce once or twice or three times.

Chrisir



//Pong sketch organized with classes

// Declare object
Paddle rightPaddle;
Ball puck;

// Global Variables
int score = 0;

void setup() {
  size (600, 600);
  noStroke();
  // Create object and pass in parameters
  rightPaddle = new Paddle();
  puck = new Ball();

  rectMode(CENTER);
  //noCursor();
}

void draw() {
  background (127);

  puck.display();
  rightPaddle.display();

  if (puck.hitPaddle(rightPaddle.pHeight)) {
    // puck.reverseX();
  }
}

// ===================================================================================================

class Ball {
  // fields
  float circX; // 0
  float circY; // 300
  float circDiameter = 20;
  float circSpeedX;
  float circSpeedY;
  int xDirection = 1;
  int yDirection = 1;

  // constructor

  Ball() {
    startBall();
  }

  // methods

  void startBall() {
    //makes ball play at a random pos and speed each time
    circX = 40;
    circY = random(circDiameter, height-200);

    circSpeedX = random(3, 5);
    circSpeedY = random(1, 2);
    if (random(100)<50) {
      circSpeedY*=-1; // not bouncing, but making the speed neg in 50%
    }
  }

  void display() {
    ellipse(circX, circY, 
      circDiameter, circDiameter);

    // Edge Collision Detection 
    leftRightCheck();
    topBottomCheck();

    circX += circSpeedX;
    circY += circSpeedY;
  }

  void leftRightCheck() {
    if (circX > width) {
      // player made an mistake 
      startBall();
    }

    if (circX < circDiameter) {
      // bounce left side
      circSpeedX = abs(circSpeedX);
    }
  }

  void topBottomCheck() {
    // TOP
    if (circY < circDiameter/2) {
      circSpeedY = abs(circSpeedY); // bounce
    }

    // Bottom
    if (circY > height - circDiameter/2) {
      circSpeedY = abs(circSpeedY) * -1; // bounce
    }
  }

  boolean hitPaddle(float paddleHeight) {
    //Ez a rész a kódomban, amivel küzdöttem, de képes voltam dolgozni. Még egyszer köszönöm a //segítségét!
    if (circX+circDiameter/2>=width-39-10&&
      circY>mouseY-paddleHeight/2&&
      circY<mouseY+paddleHeight/2) {
      circSpeedX = abs(circSpeedX) * -1; // always negative 
      //print("BOUNCE");
      return true;
    }
    return false;
  }
}

class Paddle {

  float yChange = 0;

  float pXPos = 560;
  float pYPos = 250;

  float pWidth = 20;
  float pHeight = 100;

  float constrainPY;

  // Set initial values in constructor

  Paddle() {
  }

  // Fix problem on the top of the screen so that the paddle goes all the way up.
  void display() {
    pYPos += yChange;
    float constrainPY = constrain(mouseY, 0, height);
    rect(pXPos, constrainPY, 
      pWidth, pHeight);
  }
}
//
2 Likes

Hello Chrisir,

Thank you so much for your reply. It was actually very enlightening. I had tried rectMode(CENTER) in my first draft (I’m now on my 8th draft!), but I couldn’t seem to make it work. I can see now with your example of how to use it properly. Thank you for pointing out that it’s listed in setup() so that it has a true relationship with the mouse. That makes total sense. I just started coding this year. I’m starting to realize how important it is to understand the structure of the code and how different pieces relate to each other. I’ve found it very helpful to actually draw out my code and draw arrows to outline the relationships that exist within my code.

Thanks, also, for pointing out the error with using *-1 for the bounce. Using abs() makes sense since, as you mention, the ball is still moving forward ++ even though it’s going in a negative direction. I also appreciate how you illustrate how to use *-1 properly with void startBall(). I see here that it’s not used for the bounce, but actually to control the random speed of the ball when it starts. I had to plug in exaggerated random numbers here to better understand it since this concept was a little more difficult to wrap my mind around. I’m realizing that I need to do a little math refresher to be able to master these concepts with a bit more ease.

I also really liked how you called out the puck with a dot operator under void draw() and plugged in the boolean () under the Ball class. This is brilliant and so much more organized and easier to understand. Using dot operators was such a mystery to me, but I’m starting to appreciate the advantage of using them.

Your reply has been incredibly helpful. I spent countless hours to get to this point in my pong sketch. There were some parts of my code that functioned, but I honestly didn’t completely understand why it was working. In a way, I was just happy that it worked, but it bothered me quite a bit that I didn’t understand why it worked. Thank you so much for sharing your knowledge of processing with me!

Susana

1 Like

The reason I did this:

You had random(-2,2) which can lead to 0 which is boring (ball going straight)

To avoid this: random (1,2) and then in 50% of the cases multiply by -1, so it’s either between 1 and 2 OR between -1 and -2 but not 0. Nice.

1 Like

You don’t need the if here, in fact hitPaddle doesn’t need to return anything since it does the bouncing on its own

1 Like

Haha! Yes, that is boring. Thank you for clarifying! That makes so much sense now.

Also, I noticed when you and other users post a sketch on here it’s formatted with the same color that shows up in P3. Is there a special way you’re pasting it on here. when I posted my sketch there was no color formatting and just shows up in black.

Thank you!
Susana

The posting part

Point 1: hit ctrl-t prior to copy/paste in processing : auto format of indents

Point 2: after paste select the entire code section with the mouse and click the </> sign in the small command bar

You can also go back and edit old posts : click the pen icon under the post to edit old posts

1 Like

Ahhh thank you so much for your help and for the posting tips!

I need to add a few more pieces to my pong sketch, like another ball and a score board. I actually feel pretty confidant now about how I’m going to go about tackling those.

With summer break just around the corner, I’m looking forward to having some down time to review my processing notes and check out the posts that are on this site before I have to head back to school.

Susana

1 Like