Skip to content
GimmeABrick is a spin-off of a well known classical game, Brick-Breaker. Prioritizing user interface and user experience to ensure enjoyable playing experience, the app is engineered with trigonometry to develop realistic bouncing of balls at given angle. Devised with HTML5 Canvas and HTML5 Audio, GimmeABrick promotes seemingly continuous displa…
JavaScript HTML CSS
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app/assets/javascripts
docs
images
lib
sounds
.gitignore
.jshintrc
README.md
index.html
main.css

README.md

GimmeABrick

LIVE



GimmeABrick is inspired by a well-known classical game, Brick Breaker. To it, following spin-off has been applied:

  1. There is no paddle, once the balls hit bottom, they are caught.
  2. Caught balls can be aimed and released towards the player's choice of direction.
  3. A new ball is gained every five rounds.

Notable Implementations

  1. Bricks and Balls
  2. Calculating speed and direction
  3. New levels

Bricks and Balls

Bricks and balls are essential to GimmeABrick. These two components of the game are constantly drawn on the board in an interval. While the balls are in motion, their collision with bricks are constantly tested by checking if the balls' coordinates are within the bricks' coordinates. When the coordinates of the bricks and balls overlap, they have "collided". As result of collision, bricks will change its status, being "removed" from the game. Balls will change their direction accordingly to how they hit bricks.

    drawBricks() {
      this.prepareBricks();
      for (var c = 0; c < brickColumnCount; c++ ) {
        for (var r = 0; r < brickRowCount; r++) {

          if (bricks[c][r].status > 0) {
            let brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
            let brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
            bricks[c][r].x = brickX;
            bricks[c][r].y = brickY;
            ctx.beginPath();
            ctx.fillStyle = `rgb(
              ${285 - bricks[c][r].status * 3},
              ${180 - bricks[c][r].status * 4},
              ${100 - bricks[c][r].status * 1})`;
            ctx.rect(brickX, brickY, brickWidth, brickHeight);
            ctx.fill();
          }
        }
      }
      this.gameOver();
    }
    drawBall(x, y) {
      ctx.beginPath();
      ctx.arc(x, y, radius, 0, Math.PI*2);
      ctx.fillStyle = "#0095DD";
      ctx.fill();
      ctx.closePath();
    }
    collisionDetection(ball) {
      for (var c = 0; c < brickColumnCount; c++) {
        for (var r = 0; r < brickRowCount; r++) {
          var b = bricks[c][r];
          if (b.status > 0) {
            if (ball.x > b.x - radius &&
                ball.x < b.x + brickWidth + radius &&
                ball.y > b.y - radius &&
                ball.y < b.y + brickHeight + radius) {
                b.sound.volume = volume;
                b.sound.play();
                ball.dy = -ball.dy;
                b.status -= 1;
            }}}}
    }

Calculating speed and direction

Perhaps one of the most challenging aspects of the game was to allow player to choose an angle and make sure all balls' directions are changed accordingly. One thing to note is that while their direction may change, their speed must stay constant. To implement this, good old trigonometry from high school has been revisited.

    calculateArrow() {
      radian = Math.PI / 180 * angle;
      lineX = -(Math.cos(radian) * 700 - x);
      lineY = -(Math.sin(radian) * 700 - y - radius);
    }
    calculateSpeed(angle, ball) {
      let theta = Math.atan((lineY - ball.y - radius)/(lineX - ball.x));
      let theta_a = theta * 180 / Math.PI;
      let dxValue, dyValue;
      if (theta_a > 0) {
        dxValue = -(Math.cos(theta) * speed);
        dyValue = -(Math.sin(theta) * speed);
      } else {
        dxValue = (Math.cos(theta) * speed);
        dyValue = (Math.sin(theta) * speed);
      }

      ball.dx = dxValue;
      ball.dy = dyValue;
    }

New levels

Within the game, new level is reached at certain time intervals. Upon achieving a new level, game will increase the current level by one, increase rows of bricks, and increase the number of balls every five levels.

    newLevel() {
      if (gameoverModalClosed && gameStarted) {
        level += 1;
        brickRowCount += 1;

        bricks.forEach(c => {
          c.unshift(undefined);
        });

        if (level % 5 === 0) {
          balls.push({x: canvas.width / 2, y: canvas.height - radius, dx: 0, dy: 0, sound:  new Audio('./sounds/bounce_sound.mp3')});
        }

        this.calculateArrow(angle);
        this.prepareBricks();

        if (level % 10) {
          difficulty += 0.05;
        }
      }
    }

Future Implementations & thoughts

For future additions, the game can use an option to set difficulty level. Choosing a certain difficulty level should increase durability of the bricks, increase level interval when a new ball is achieved, and increasing the number of rows gained.

You can’t perform that action at this time.