My first game of pong and some questions

I’m going to show the code of what I’ve done and then I’ll ask questions one at a time.

//pong
int point = 0;
Player player;
PlayerCpu playerCpu;
Ball ball;

void settings(){
  size(640,480,P2D);
  noSmooth();
}

void setup(){
  frameRate(60);
  
  player = new Player();
  playerCpu = new PlayerCpu();
  ball = new Ball();
}

void draw(){
  //fondo-----------------
  background(0,0,0);
  //texto-----------------
  textSize(24);
  textAlign(CENTER);
  fill(color(255,255,255));
  text("Puntos: "+point,320,32);
  //objetos-------------------
  player.Update();
  playerCpu.Update();
  ball.Update();
}

//funcion para colisiones--------------------------------
boolean collisionRect(float x1,float y1,float w1,float h1,float x2,float y2,float w2,float h2){
  return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}
public class Ball{
  private float x,y,w,h;
  private int speedX,speedY;
  private color colors;
  private boolean live;
  
  public Ball(){
    this.w = 14;
    this.h = 14;
    this.x = 320 - this.w / 2;
    this.y = 240 - this.h / 2;
    this.speedX = 2;
    this.speedY = 2;
    this.colors = color(255,255,255);
    this.live = true;
  }
  
  public void Update(){
    if(live == true){
      Move();
      Bounce();
      Collision();
      Draw();
    }
  }
  
  private void Draw(){
    fill(colors);
    rect(x,y,w,h);
  }
  
  private void Move(){
    x += speedX;
    y += speedY;
  }
  
  private void Bounce(){
    if(x <= 0 || x >= 640){
      speedX *= -1; 
    }
    
    if(y <= 0 || y >= 480){
      speedY *= -1; 
    }
  }
  
  private void Collision(){
    //player
    if(collisionRect(x,y,w,h,player.x,player.y,player.w,player.h)){
      speedX *= -1;
      //speedX = abs(speedX);
    } 
    //playeCpu
    if(collisionRect(x,y,w,h,playerCpu.x,playerCpu.y,playerCpu.w,playerCpu.h)){
      speedX *= -1;
    }
  }
}
public class Player{
  private float x,y,w,h;
  private int speed;
  private color colors;
  private boolean live;
  
  public Player(){
    this.w = 20;
    this.h = 100;
    this.x = 32 - this.w / 2;
    this.y = 240 - this.h / 2;
    this.speed = 5;
    this.colors = color(255,0,204);
    this.live = true;
  }
  
  public void Update(){
    if(live == true){
      Move();
      Draw();
    }
  } 
  
  private void Draw(){
    fill(colors);
    rect(x,y,w,h);
  }
  
  private void Move(){
    if(keyPressed && key == CODED && keyCode == UP && y > 16){
      y -= speed;
    }else if(keyPressed && key == CODED && keyCode == DOWN && y < 370){
      y += speed;
    }
  }
}//fin clase player
public class PlayerCpu{
  private float x,y,w,h;
  private int speed;
  private color colors;
  private boolean live;
  
  public PlayerCpu(){
    this.w = 20;
    this.h = 100;
    this.x = 608 - this.w / 2;
    this.y = 240 - this.h / 2;
    this.speed = 5;
    this.colors = color(255,204,0);
    this.live = true;
  }
  
  public void Update(){
    if(live == true){
      Move();
      Draw();
    }
  }
  
  private void Draw(){
    fill(colors);
    rect(x,y,w,h);
  }
  
  private void Move(){
    if(y > ball.y && y > 16){
      y -= speed;
    }
    
    if(y < ball.y && y < 370){
      y += speed;
    }
  }
}//fin clase player cpu

The first question is about collision detection, I am using the collision function that is in the main file “collisionRect” and there seems to be a problem when the ball collides with the top or bottom of the player, it does not bounce but rather It moves downwards when colliding and I don’t know how to fix it.

Let’s take a look at your Collision() function:

(Side note: Usually functions start with a lower-case letter, in this case it would be collision() to make it a little easier to read.)

In this function, you check whether the ball is colliding with the player, and if it is, you then reverse its speedX variable. In other words, the ball can only bounce left and right, never up or down.

Now imagine what happens when the ball intersects with the top of the player’s rectangle, like this:

Your code detects the collision, and detects it, so it reverses the xSpeed variable. In the next frame, the ball continues moving down, and will also begin moving to the right. But because it’s still moving down, the ball is still colliding with the player, so your code reverse the xSpeed variable again. Now it’s still moving down, and a little to the left.

This process repeats, where the ball continues to move down, and “bounces” left and right by xSpeed every frame.

To fix this, I’d recommend thinking about exactly what you want to happen. Do you need to detect which side of the rectangle the ball intersected? If so, I wrote a tutorial on that here:

Specifically, see the Collision Detection with Moving Objects section on that page.

Alternatively, can you treat the paddles as lines instead of rectangles?

1 Like

I already have it, I have added the speed in x to the “X” of the ball, I have also added the speed y to the “Y” of the ball in the collision function.

Additionally, I have used the collision function twice, once for the “X” and once for the “Y”.

//player colision rectangulo con rectangulo
if(collisionRect(x+speedX,y,w,h,player.x,player.y,player.w,player.h)){
      speedX *= -1;
    }
    if(collisionRect(x,y+speedY,w,h,player.x,player.y,player.w,player.h)){
      speedY *= -1;
    }
    
    //playeCpu colision rectangulo con rectangulo
    if(collisionRect(x+speedX,y,w,h,playerCpu.x,playerCpu.y,playerCpu.w,playerCpu.h)){
      speedX *= -1;
    }
    if(collisionRect(x,y+speedY,w,h,playerCpu.x,playerCpu.y,playerCpu.w,playerCpu.h)){
      speedY *= -1;
    }

Second question.
The AI that the player who controls the CPU has is quite simple and quite boring to play. Could you tell me how to improve the AI of the CPU so that the movements are as close as if a human were controlling it.

Nice, glad you got it figured out.

For making the AI feel more like a human, I’d recommend taking a step back and thinking about what it means for a pong player to look like a human. What kinds of behaviors does a human exhibit? How might you start implementing those with code?

For example, right now it looks like your AI player always looks at the current position of the ball to change its position, and it updates its position every frame. What would a human player do instead?

What I do is wait until the ball is close to my paddle to see which direction the ball is going to go and then I move the paddle in those directions.

I also pay attention to when it bounces off the top of the screen or the bottom, when I see that it is heading towards me I move the paddle in that direction.

That makes sense to me. So next, I’d think about how you might represent that with code. There isn’t a right or wrong answer here, and you’ll probably have to play around with different approaches!

For example: instead of updating the direction of the computer’s paddle every frame, could you update it once per second instead?

I have added the distance calculation, with this the AI of the CPU palette has improved a little. At the moment it is fine, I will see if I can think of something else to improve it later.

private void Move(){
    if(dist(x,y,ball.x,ball.y) < 256){
      if(y > ball.y && y > 16){
        y -= speed;
      }
      
      if(y < ball.y && y < 370){
        y += speed;
      }
    }
  }

Third and last question.

How can I make my game pause. I want text to appear on the screen that says “pause” when I press the “p” button and for the game to be paused, then when I press the “p” button again, the text will disappear and the game will move again.

Nice! It looks like you already know how to detect key presses, so I think you know how to take an action when p is pressed.

For pausing, you might add a boolean variable isPlaying or isPaused at the top of your sketch. Then in your draw() function, you can check the value of that variable to decide what to do based on whether the game is currently paused or not.

Does that make sense?

I already have it, although there is a problem.

When the text “pause” appears, it is continually being painted and the letters are increasingly blurred. If I use background() to clear the screen, the text will be displayed correctly, but the game objects disappear and I don’t want that.

I want the game objects to also be seen when the pause text appears. How is this done?

//pong
int point = 0;
boolean isPaused = false;
Player player;
PlayerCpu playerCpu;
Ball ball;

void settings(){
  size(640,480,P2D);
  noSmooth();
}

void setup(){
  frameRate(60);
  
  player = new Player();
  playerCpu = new PlayerCpu();
  ball = new Ball();
}

void draw(){
  if(isPaused == false){
    //fondo-----------------
    background(0,0,0);
    //texto puntos----------------
    textSize(24);
    textAlign(CENTER);
    fill(color(255,255,255));
    text("Puntos: "+point,320,32);
    //objetos-------------------
    player.Update();
    playerCpu.Update();
    ball.Update();
  }else{
    textSize(64);
    textAlign(CENTER);
    fill(color(0,255,255));
    text("Pause",320,240);
  }
}

void keyPressed(){
  if(key == 'p'){
    isPaused = !isPaused;
  }
}

//funcion para colisiones--------------------------------
boolean collisionRect(float x1,float y1,float w1,float h1,float x2,float y2,float w2,float h2){
  return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}

It sounds like when the game is paused, you want to draw your player and your opponent, but not update them, right?

When does the code for drawing your game objects run? When does the code for updating your game objects run? Could you rearrange them to only draw your objects, but not update them?

(I feel like you’re really close and I don’t want to just give it away, because I bet you can figure it out!)

I think I already have it.

void draw(){
  if(isPaused == false){
    //fondo-----------------
    background(0,0,0);
    //texto puntos----------------
    textSize(24);
    textAlign(CENTER);
    fill(color(255,255,255));
    text("Puntos: "+point,320,32);
    //objetos-------------------
    player.Update();
    player.Draw();
    playerCpu.Update();
    playerCpu.Draw();
    ball.Update();
    ball.Draw();
  }else{
    background(0,0,0);
    player.Draw();
    playerCpu.Draw();
    ball.Draw();
    textSize(64);
    textAlign(CENTER);
    fill(color(0,255,255));
    text("Pause",320,240);
  }
}

I have discovered an error in the collision of the ball with the player.
If the player does not move, the collision and bounce works fine, but if the player moves the ball colliding with the top and bottom of the player does not bounce.

How to solve this error?

My guess is it’s a similar problem to before: you change the direction of the ball, but you don’t change its position. So if the paddle is moving in the same direction as the ball, on the next frame the ball will still be colliding with the paddle.

You might try “snapping” the ball to a position to make sure it’s outside the bounds of the paddle. Alternatively, you wouldn’t have to worry about these cases if you treated the paddles as lines instead of rectangles.

In which part of the code do I have to adjust the position of the ball and how, adding a value to x and y or how exactly.

How I treat the palettes as lines, drawing lines instead of rectangles. I have done it but the lines are very little visible and I cannot change their color, and I cannot move them, they only grow and decrease.

Or maybe you mean using a collision between lines instead of between rectangles, because I don’t see that function in your tutorial.

You would want to adjust the position of the ball whenever it intersects with the paddle. I wrote about this in the Snapping to an Edge section here:

You can still draw the paddles as rectangles, but then treat them as lines when you check whether the ball went outside the game bounds.

Right, exactly. The code for that would be something like this:

if(ballX < leftEdge) {
  if (ballY > paddleY && ballY < paddleY + paddleHeight) {
    // bounce
  } else {
    // score a point
  }
}

That’s just an example, but the idea would be the same.

1 Like

Thank you very much for all the help. :slightly_smiling_face: :+1:

1 Like