10
\$\begingroup\$

I decided to make a Breakout game in JavaScript. Is there a way of making it cleaner? For instance, creating objects for the ball and the paddle.

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Brick Game</title>
 <style type="text/css">
 body {
 background-color: black;
 }
 canvas {
 border: 1px solid green;
 }
 </style>
</head>
<body>
 <canvas id="game-canvas" height="600px" width="800px" </canvas>
 <script type="text/javascript">
 var canvas = document.getElementById("game-canvas");
 // Get a 2D context for the canvas.
 var ctx = canvas.getContext("2d");
 var ballR = 10;
 var x = canvas.width / 2;
 var y = canvas.height - 30;
 var dx = 3;
 var dy = -3;
 var pongH = 15;
 var pongW = 80;
 var pongX = (canvas.width - pongW) / 2;
 var rightKey = false;
 var leftKey = false;
 var brickRows = 3;
 var brickCol = 9;
 var brickW = 75;
 var brickH = 20;
 var brickPadding = 10;
 var brickOffsetTop = 30;
 var brickOffsetLeft = 30;
 var bricks = [];
 for (c = 0; c < brickCol; c++) {
 bricks[c] = [];
 for (r = 0; r < brickRows; r++) {
 bricks[c][r] = {
 x: 0,
 y: 0,
 status: 1
 };
 }
 }
 // function to draw the ball 
 function drawBall() {
 ctx.beginPath();
 ctx.arc(x, y, ballR, 0, Math.PI * 2);
 ctx.fillStyle = "red";
 ctx.fill();
 ctx.closePath();
 }
 // function draw the pong
 function drawPong() {
 ctx.beginPath();
 ctx.rect(pongX, canvas.height - pongH, pongW, pongH);
 ctx.fillStyle = "blue";
 ctx.fill();
 ctx.closePath();
 }
 // function draw the bricks
 function drawBricks() {
 for (c = 0; c < brickCol; c++) {
 for (r = 0; r < brickRows; r++) {
 if (bricks[c][r].status == 1) {
 var brickX = (c * (brickW + brickPadding)) + brickOffsetLeft;
 var brickY = (r * (brickH + brickPadding)) + brickOffsetTop;
 bricks[c][r].x = brickX;
 bricks[c][r].y = brickY;
 ctx.beginPath();
 ctx.rect(brickX, brickY, brickW, brickH);
 ctx.fillStyle = "green";
 ctx.fill();
 ctx.closePath();
 }
 }
 }
 }
 function collisionDetection() {
 for (c = 0; c < brickCol; c++) {
 for (r = 0; r < brickRows; r++) {
 var b = bricks[c][r];
 if (b.status == 1) {
 if (x > b.x && x < b.x + brickW && y > b.y && y < b.y + brickH) {
 dy = -dy;
 b.status = 0;
 }
 }
 }
 }
 }
 function draw() {
 ctx.clearRect(0, 0, canvas.width, canvas.height);
 drawBricks();
 drawBall();
 drawPong();
 collisionDetection();
 if (x + dx > canvas.width - ballR || x + dx < ballR) {
 dx = -dx;
 }
 if (y + dy < ballR) {
 dy = -dy;
 } else if (y + dy > canvas.height - ballR) {
 if (x > pongX && x < pongX + pongW) {
 dy = -dy;
 } else {
 // if the ball hits the bottom of canvas
 // reload the game
 document.location.reload();
 }
 }
 // when key is pressed 
 function keyDown(e) {
 if (e.keyCode == 39) {
 rightKey = true;
 } else if (e.keyCode == 37) {
 leftKey = true;
 }
 }
 // when key is not pressed
 function keyUp(e) {
 if (e.keyCode == 39) {
 rightKey = false;
 } else if (e.keyCode == 37) {
 leftKey = false;
 }
 }
 // Add an event listener to the keypress event.
 document.addEventListener("keydown", keyDown, false);
 document.addEventListener("keyup", keyUp, false);
 // move the pong right if the right key pressed
 if (rightKey && pongX < canvas.width - pongW) {
 pongX += 7;
 }
 // move the pong left if the left key pressed
 else if (leftKey && pongX > 0) {
 pongX -= 7;
 }
 x += dx;
 y += dy;
 }
 setInterval(draw, 10);
 </script>
</body>
</html>

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Nov 4, 2015 at 14:46
\$\endgroup\$
0

1 Answer 1

4
\$\begingroup\$

First, great job. I loved seeing that the whole game had been created with so little code, and was so easy to understand.

While you could consider making this more object-oriented, I don't think it's necessary, so I did some rewrites in keeping with the simple procedural style of the original.

The biggest place for improvement is in naming your concepts. For example, this:

if (hitSideWall())
 dx = -dx;
if (hitTop() || hitPong())
 dy = -dy;
if (gameOver())
 document.location.reload();

is much clearer than:

if (y + dy < ballR) {
 dy = -dy;
} else if (y + dy > canvas.height - ballR) {
 if (x > pongX && x < pongX + pongW) {
 dy = -dy;
 } else {
 document.location.reload();
 }
}

You can apply this principle over and over, at all levels of your code. See the full rewrite for more examples.

Other notes:

  • Avoid nesting wherever possible, and avoid "if... else" statements.
  • The nested for loop for the bricks only needs to be done once. Use it once to create a flat array of bricks. Afterward, the nested structure is superfluous, since each brick object contains the info you need. Just loop through the flat array with forEach.
  • When initializing multiple variables, just use one var and commas.

Full rewrite:

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Brick Game</title>
 <style type="text/css">
 body { background-color: black; }
 canvas { border: 1px solid green; }
 </style>
</head>
<body>
 <canvas id="game-canvas" height="600px" width="800px" </canvas>
 <script type="text/javascript">
 var canvas = document.getElementById("game-canvas"),
 ctx = canvas.getContext("2d"),
 ballR = 10,
 x = canvas.width / 2,
 y = canvas.height - 30,
 dx = 3,
 dy = -3,
 pongH = 15,
 pongW = 80,
 pongX = (canvas.width - pongW) / 2,
 rightKey = false,
 leftKey = false,
 brickRows = 3,
 brickCol = 9,
 brickW = 75,
 brickH = 20,
 brickPadding = 10,
 brickOffsetTop = 30,
 brickOffsetLeft = 30;
 var bricks = [];
 for (c = 0; c < brickCol; c++) {
 for (r = 0; r < brickRows; r++) {
 bricks.push({
 x: (c * (brickW + brickPadding)) + brickOffsetLeft,
 y: (r * (brickH + brickPadding)) + brickOffsetTop,
 status: 1
 });
 }
 }
 // function to draw the ball 
 function drawBall() {
 ctx.beginPath();
 ctx.arc(x, y, ballR, 0, Math.PI * 2);
 ctx.fillStyle = "red";
 ctx.fill();
 ctx.closePath();
 }
 // function draw the pong
 function drawPong() {
 ctx.beginPath();
 ctx.rect(pongX, canvas.height - pongH, pongW, pongH);
 ctx.fillStyle = "blue";
 ctx.fill();
 ctx.closePath();
 }
 // function draw the bricks
 function drawBricks() {
 bricks.forEach(function(brick) {
 if (!brick.status) return;
 ctx.beginPath();
 ctx.rect(brick.x, brick.y, brickW, brickH);
 ctx.fillStyle = "green";
 ctx.fill();
 ctx.closePath();
 });
 }
 function collisionDetection() {
 bricks.forEach(function(b) {
 if (!b.status) return;
 var inBricksColumn = x > b.x && x < b.x + brickW,
 inBricksRow = y > b.y && y < b.y + brickH;
 if (inBricksColumn && inBricksRow) {
 dy = -dy;
 b.status = 0;
 }
 });
 }
 function draw() {
 ctx.clearRect(0, 0, canvas.width, canvas.height);
 drawBricks();
 drawBall();
 drawPong();
 collisionDetection();
 if (hitSideWall())
 dx = -dx;
 if (hitTop() || hitPong())
 dy = -dy;
 if (gameOver())
 document.location.reload();
 var RIGHT_ARROW = 39,
 LEFT_ARROW= 37;
 function hitPong() { return hitBottom() && ballOverPong() }
 function ballOverPong() { return x > pongX && x < pongX + pongW }
 function hitBottom() { return y + dy > canvas.height - ballR }
 function gameOver() { return hitBottom() && !ballOverPong() }
 function hitSideWall() { return x + dx > canvas.width - ballR || x + dx < ballR }
 function hitTop() { return y + dy < ballR }
 function xOutOfBounds() { return x + dx > canvas.width - ballR || x + dx < ballR }
 function rightPressed(e) { return e.keyCode == RIGHT_ARROW }
 function leftPressed(e) { return e.keyCode == LEFT_ARROW }
 function keyDown(e) {
 rightKey = rightPressed(e); 
 leftKey = leftPressed(e);
 }
 function keyUp(e) {
 rightKey = rightPressed(e) ? false : rightKey;
 leftKey = leftPressed(e) ? false : leftKey;
 }
 // Add an event listener to the keypress event.
 document.addEventListener("keydown", keyDown, false);
 document.addEventListener("keyup", keyUp, false);
 // move the pong right if the right key pressed
 var maxX = canvas.width - pongW,
 minX = 0,
 pongDelta = rightKey ? 7 : leftKey ? -7 : 0;
 pongX = pongX + pongDelta;
 pongX = Math.min(pongX, maxX);
 pongX = Math.max(pongX, minX);
 x += dx;
 y += dy;
 }
 setInterval(draw, 10);
 </script>
</body>
</html>
answered Nov 5, 2015 at 5:02
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.