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>

\$\endgroup\$
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>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.