Creating a "simple" game in P5.js and am stuck on one issue

103 Views Asked by At

I am creating(or recreating) a classic style game for a final project for my art school class. Coding is new. I am about 8 months into learning it so, everything is still in "training-wheel" mode for me. I think the game was called paddle ball. It is like pong but with one "ball" and one "paddle" the ball bounced off the top and side. The player tried to return the ball with the paddle and if it goes past, the player loses and the game restarts.

THe problem I am having is the when the ball come in contact with the paddle, in has suddenly been getting stuck and not bounding back. If the paddle stays in contact, it rapidly raises the "score". Also, now the paddle is moving on the y axis then it is supposed to be fixed and only move on the x axis.

I have been fiddling with it so much, I am sure I accidently save over something I didn't intend to.

Could someone please take a peek and show me how to fix it back to working again?

Here is a link to the code (runswithout errors here): https://editor.p5js.org/Rico2022/sketches/YIOKSbFWu

Below I will put the code as well:

// Requirement 1: Load Sound
let sound;

function preload() {
  //sound = loadSound('metalPing4.mp3');
}

// Requirement 7: Define a custom class
class Ball {
  constructor(x, y, r) {
    this.x = x;
    this.y = y;
    this.r = r;
    this.xSpeed = random(2, 7) * (random() > 0.5 ? 1 : -1);
    this.ySpeed = random(-7, -2);
  }

  move() {
    this.x += this.xSpeed;
    this.y += this.ySpeed;
  }

  display() {
    fill('#D9FFF5');
    ellipse(this.x, this.y, this.r * 2, this.r * 2);
  }

  bounce() {
    if (this.x < this.r || this.x > width - this.r) {
      this.xSpeed *= -1;
    }
    if (this.y < this.r || this.y > height - this.r) {
      this.ySpeed *= -1;
    }
  }
}

let ball;
let score = 0;
let gameState = "start";

function setup() {
  createCanvas(400, 400);
  ball = new Ball(random(50, 350), 50, 10);
}

function draw() {
  background(0);

  if (gameState === "start") {
    titleScreen();
  } else if (gameState === "playing") {
    playGame();
  } else if (gameState === "playAgain") {
    playAgainScreen();
  }
}

function titleScreen() {
  fill('#d9c3f7');
  textSize(32);
  textAlign(CENTER);
  text("Paddle Ball Game", width / 2, height / 2 - 40);
  textSize(24);
  text("Click to Start", width / 2, height / 2);
}

function playGame() {
  // Requirement 5: Use sin or cos
  let paddleY = height - 25 - 15 * sin(radians(frameCount % 360));

  // Paddle
  fill('#ffffff');
  rect(mouseX, paddleY, 90, 15);

  ball.move();
  ball.display();
  ball.bounce();
  paddleCollision(paddleY);

  //Score
  fill('#d9c3f7');
  textSize(24);
  text("Score: " + score, 10, 25);
}

function playAgainScreen() {
  fill('#d9c3f7');
  textSize(32);
  textAlign(CENTER);
  text("Game Over", width / 2, height / 2 - 40);
  textSize(24);
  text("Click to Play Again", width / 2, height / 2);
}

function mousePressed() {
  if (gameState === "start") {
    gameState = "playing";
  } else if (gameState === "playAgain") {
    gameState = "playing";
    score = 0;
    ball = new Ball(random(50, 350), 50, 10);
  }
}

// Requirement 6: Create and use your own function
function paddleCollision(paddleY) {
  if (ball.x > mouseX && ball.x < mouseX + 90 && ball.y + ball.r >= paddleY) {
    // Requirement 2: Play Sound
    //sound.play();

    // Requirement 4: Use of map()
    let angle = map(ball.x, mouseX, mouseX + 90, -PI / 4, PI / 4);
    ball.xSpeed = ball.xSpeed * cos(angle) * 1.05;
    ball.ySpeed = -abs(ball.ySpeed) * sin(angle) * 1.05;

    // Requirement 10: Event on Collision
    score++;
  }
}

// Requirement 3: Use random
function randomBgColor() {
  let r = random(0, 255);
  let g = random(0, 255);
  let b = random(0, 255);
  return color(r, g, b);
}

function titleScreen() {
  //background(randomBgColor());
  background(107, 143, 113);
  fill('#d9c3f7');
  textSize(32);
  textAlign(CENTER);
  text("Paddle Ball Game", width / 2, height / 2 - 40);
  textSize(24);
  text("Click to Start", width / 2, height / 2);
}

function playGame() {
  // Requirement 5: Use sin or cos
  let paddleY = height - 25 - 15 * sin(radians(frameCount % 360));

  // Paddle
  fill('#AAD2BA');
  rect(mouseX, paddleY, 90, 15);

  ball.move();
  ball.display();
  ball.bounce();
  paddleCollision(paddleY);

  //Score
  fill('#d9c3f7');
  textSize(24);
  text("Score: " + score, 60, 25);

  // Requirement 12: Playability
  if (ball.y + ball.r >= height) {
    gameState = "playAgain";
  }
}

function playAgainScreen() {
  //background(randomBgColor());
  background(185, 245, 216);
  fill('#d9c3f7');
  textSize(32);
  textAlign(CENTER);
  text("Game Over", width / 2, height / 2 - 40);
  textSize(24);
  text("Click to Play Again", width / 2, height / 2);
}

// Requirement 11: User Input
function keyPressed() {
  if (key === 'P' || key === 'p') {
    if (gameState === "playing") {
      gameState = "paused";
    } else if (gameState === "paused") {
      gameState = "playing";
    }
  }
}
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
</script>
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>

1

There are 1 best solutions below

0
wonderflame On

The problem is that you are changing ball's both speeds(x,y) during the collision with sin, cos functions, whereas you have to change y speed. The origin of the coordinates is in the top left corner. As the paddle is always at the bottom, it means during the collision, you have to change y to a positive value, which can be done as follows:

ball.ySpeed = abs(ball.ySpeed) * -1;

editor

Also, since the paddle also moves along the y axis, you may have collision triggered several times, which would spam the score. We can be confident that in the ideal case, the ball can't collide with the paddle two times in a row. Therefore, we will add a boolean flag that indicates if the previous collision was with the paddle or not. The initial value should be false:

class Ball {
  constructor(x, y, r) {
    this.x = x;
    this.y = y;
    this.r = r;
    this.xSpeed = random(2, 7) * (random() > 0.5 ? 1 : -1);
    this.ySpeed = random(-7, -2);
    this.isLastCollisionWithPaddle = false;
  }

Then in the ball.bounce(), if we touch the borders we will set the isLastCollisionWithPaddle to false:

  bounce() {
    if (this.x < this.r || this.x > width - this.r) {
      this.xSpeed *= -1;
      this.isLastCollisionWithPaddle = false;
    }
    if (this.y < this.r || this.y > height - this.r) {
      this.ySpeed *= -1;
      this.isLastCollisionWithPaddle = false;
    }
  }

The remaining part is to make sure that we only change the speed and score if and only if the previous collision wasn't with the paddle:

function paddleCollision(paddleY) {
  if (ball.x > mouseX && ball.x < mouseX + 90 && ball.y + ball.r >= paddleY && !ball.isLastCollisionWithPaddle) {
    // Requirement 2: Play Sound
    sound.play();
    
    ball.isLastCollisionWithPaddle = true;

    // Requirement 4: Use of map()
    let angle = map(ball.x, mouseX, mouseX + 90, -PI / 4, PI / 4);
    ball.ySpeed = abs(ball.ySpeed) * -1;

    // Requirement 10: Event on Collision
    score++;
  }
}

editor