1
votes

I'm currently trying to make a version of Breakout for university. Thanks for helping me to draw the paddle. I now find myself unable to cause the ball to bounce on the edge of the canvas - except in the middle. I've tried both adding and subtracting fractions to ball.x and ball.y (greater than or equal to canvas.width and canvas.height both work) but for less than or equal to 0, nothing seems to be successful. Here's the javascript code:

    var canvas = document.getElementById("breakout");
var ctx = canvas.getContext("2d");
var PADDLE_WIDTH_PX = canvas.width / 5;
var PADDLE_HEIGHT_PX = 10;
var PADDLE_SPEED = 450;

var ball = {
  x: canvas.width / 2,   //pixels
  y: canvas.height / 2,  //pixels
  xSpeed: 500,           //pixels per second
  ySpeed: 500,           //pixels per second
  radius: 100  //the ball is exceptionally large so that I can see what part of the ball is surpassing the canvas edge before the motion is reversed
}

var paddle = {
//radius: 5,
/*speed: 500,
TopRight: ctx.moveTo(canvas.width / 1.35, canvas.height - (canvas.height / 12.5)),
TopSide: ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5)),
RightSide: ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5)),
BottomLeft: ctx.moveTo(canvas.width / 2, canvas.height - (canvas.height / 27.5)),
LeftSide: ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5)),
BottomSide: ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5))*/
xSpeed: 450,
x: (canvas.width - PADDLE_WIDTH_PX) / 2,
y: canvas.height - PADDLE_HEIGHT_PX
}

var keysDown = {};
window.addEventListener("keydown",function(e) {
keysDown[e.keyCode] = true;
});
window.addEventListener("keyup",function(e) {
delete keysDown[e.keyCode];
});


function render() {
  //clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  // draw the ball
  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
  ctx.closePath();
  ctx.fill();
  ctx.beginPath();
  //ctx.fillStyle = "red";
  /*ctx.moveTo(canvas.width - (2*paddle.x), canvas.height - (2*paddle.y));
  /*ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5));
  ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5));
  ctx.moveTo(canvas.width / 2, canvas.height - (canvas.height / 27.5));
  ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5));
  ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5));
  ctx.fill();
  ctx.closePath();*/
  /*ctx.lineTo(canvas.width - (2*paddle.x), canvas.height - paddle.y);
  ctx.moveTo(canvas.width - paddle.x, canvas.height - paddle.y);
  ctx.lineTo(canvas.width - paddle.x, canvas.height - (2*paddle.y));
  ctx.lineTo(canvas.width - (2*paddle.x), (canvas.height -paddle.y));*/
  ctx.fillRect(paddle.x, paddle.y, PADDLE_WIDTH_PX, PADDLE_HEIGHT_PX);
  /*ctx.closePath();
  ctx.fill();*/
}

function update(elapsed) {
  //update the ball position according to the elapsed time
  ball.y += ball.ySpeed * elapsed;
  ball.x += ball.xSpeed * elapsed;
  /*paddle.TopRight += paddle.speed * elapsed;
  paddle.BottomLeft += paddle.speed * elapsed;
  paddle.RightSide += paddle.speed * elapsed;
  paddle.LeftSide += paddle.speed * elapsed;
  paddle.TopSide += paddle.speed * elapsed;
  paddle.BottomSide += paddle.speed * elapsed;*/
  /*paddle.x += paddle.xSpeed * elapsed;
  paddle.y += paddle.xSpeed * elapsed;*/

  //bounce the ball of all edges
if (37 in keysDown && paddle.x > 0)
  paddle.x -= PADDLE_SPEED * elapsed;
if (39 in keysDown && paddle.x + PADDLE_WIDTH_PX < canvas.width)
  paddle.x += PADDLE_SPEED * elapsed;

  if (ball.x+(ball.x/7) >= canvas.width) {
    ball.x -= 5;
    ball.xSpeed *= -1;
  }
  if (ball.x-(ball.x/7) <= 0) {
      ball.x += 5;
      ball.xSpeed *= -1;
  }
  if (ball.y+(ball.y/100) <= 0) {
    ball.y += 5;
    ball.ySpeed *= -1;
  }
  if (ball.y+(ball.y/3) >= canvas.height) {
    ball.y -= 5;
    ball.ySpeed *= -1;
    }

  /*
  The problem here is that sometimes the ball gets 'stuck' to an edge.

  This can occur when the ball passes beyond an edge in a frame when the
  elapsed time is relatively large. In some cases, when the elapsed time in the
  next frame is relatively short, the ball doesn't reach the edge to get back
  into play. This results in another flip of the velocity and the ball becomes
  'trapped' on the edge.

  e.g.
  xSpeed = -500, x = 10, elapsed = 0.2 => xSpeed = 500, x = -90 (xMovement = -100)
  xSpeed = 500, x = -90, elapsed = 0.1 => xSpeed = -500, x = -40 (xMovement = +50)
  xSpeed = -500, x = -40, elapsed = 0.1 => xSpeed = 500, x = -40 (xMovement = -50)
  and so on ...until a larger elapsed time occurs in the right direction

  The fix for this is to move the ball to the edge when the velocity is flipped.
  */
}

var previous;
function run(timestamp) {
  if (!previous) previous = timestamp;          //start with no elapsed time
  var elapsed = (timestamp - previous) / 1000;  //work out the elapsed time
  update(elapsed);                              //update the game with the elapsed time
  render();                                     //render the scene
  previous = timestamp;                         //set the (globally defined) previous timestamp ready for next time
  window.requestAnimationFrame(run);            //ask browser to call this function again, when it's ready
}

//trigger the game loop
window.requestAnimationFrame(run);

Thanks for taking the time to read this --ConfusedStudent

1

1 Answers

0
votes

There is a number of issues with the paddle.

First of all you probably want it to be of a fixed size, so let's define its dimensions at the beginning of the file (put it after the first two lines of your code because it uses the canvas to set the paddle width to 1/5th of its width - I think that's what you tried to do):

var PADDLE_WIDTH_PX = canvas.width / 5;
var PADDLE_HEIGHT_PX = 10;

With this you can initialize the paddle to be at the bottom of the canvas and in the middle:

var paddle = {
  x: (canvas.width - PADDLE_WIDTH_PX) / 2,
  y: canvas.height - PADDLE_HEIGHT_PX
}

x and y are the top-left corner of the paddle, so the right side is at x + PADDLE_WIDTH_PX and the bottom side is at y + PADDLE_HEIGHT_PX.
Knowing this, you can draw a path throught all the four corners like this:

ctx.beginPath();
ctx.moveTo(paddle.x, paddle.y);
ctx.lineTo(paddle.x + PADDLE_WIDTH_PX, paddle.y);
ctx.lineTo(paddle.x + PADDLE_WIDTH_PX, paddle.y + PADDLE_HEIGHT_PX);
ctx.lineTo(paddle.x, paddle.y + PADDLE_HEIGHT_PX);
ctx.lineTo(paddle.x, paddle.y);
ctx.closePath();

But since the paddle is just a rectangle, it's easier to use a method for drawing rectangles - fillRect, like this:

ctx.fillRect(paddle.x, paddle.y, PADDLE_WIDTH_PX, PADDLE_HEIGHT_PX);

Either way, all the four corners of the paddle move toggether so it doesn't grow or shrink.

So if you put it in your render function, it looks like this:

function render() {
  //clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  // draw the ball
  ctx.fillStyle = "white";
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
  ctx.closePath();
  ctx.fill();

  // draw the paddle
  ctx.fillStyle = "red";
  ctx.fillRect(paddle.x, paddle.y, PADDLE_WIDTH_PX, PADDLE_HEIGHT_PX);
}

The last thing is to get the paddle to move when the left and right arrow keys are pressed.

The paddle only moves when either arrow left or arrow right is pressed. Otherwise its speed is 0 and it sits in its place. Therefore the paddle object doesn't need the xSpeed member variable.
Also, the paddle only moves horizontally so only its x variable changes, y is always the same.

Let's first define the speed of the paddle at the beginning of the file:

var PADDLE_SPEED = 300;

and then let's put the movement logic in the update function:

if (37 in keysDown && paddle.x > 0)
  paddle.x -= PADDLE_SPEED * elapsed;
else if (39 in keysDown && paddle.x + PADDLE_WIDTH_PX < canvas.width)
  paddle.x += PADDLE_SPEED * elapsed;

You can notice that the paddle position is only changed if an arrow key is pressed and the paddle is not at the edge.

All the other code that deals with the paddle should be removed from the update function so that it looks like this (I have removed most of the comments):

function update(elapsed) {
  //update the ball position according to the elapsed time
  ball.y += ball.ySpeed * elapsed;
  ball.x += ball.xSpeed * elapsed;

  if (37 in keysDown && paddle.x > 0)
    paddle.x -= PADDLE_SPEED * elapsed;
  else if (39 in keysDown && paddle.x + PADDLE_WIDTH_PX < canvas.width)
    paddle.x += PADDLE_SPEED * elapsed;

  //bounce the ball of all edges
  if/*(*/(ball.x /*- (ball.x / 2))*/<= 0) {
    ball.x = 1;
    ball.xSpeed *= -1;  
  }
  if /*(*/(ball.x /*+ (ball.x / 2))*/>= canvas.width) {
    ball.x = ball.x -1;
    ball.xSpeed *= -1;
  }
  if/*(*/(ball.y /*- (ball.y / 2))*/<= 0) {
    ball.y = 1;
    ball.ySpeed *= -1;
  }
  if /*(*/(ball.y /*+ (ball.y / 2))*/>= canvas.height) {
    ball.y = ball.y -1;
    ball.ySpeed *= -1;
  }
}