Making A Block It Game Prototype With HTML5

In this tutorial we are creating a protoype of a game like the mobile game Block It from Ketchapp Games. Block It is an action game that requires fast reflexes from the player. The game consist of a moving ball that is enclosed in a room with three walls. When the player touches the screen, a fourth wall appears. The goal of the game is to block the ball from moving out of the room. The player can prevent the ball from escaping the room by touching the screen at the right moment, because when the ball collides with a wall, the ball is bounced back. Every time the player successfully blocks the ball, the speed of the ball increases. As the game progresses, it becomes harder and harder to keep the ball from escaping the room.

Making a game with the mechanics of Block It is pretty easy. In my tutorial How To Make An HTML5 Canvas Game we have already seen how to make a game with a bouncing square inside a room. The game we are making in this tutorial is only a bit more complicated, but most of the code it pretty similar.

Making A Block It Game Prototype With HTML5

Making A Block It Game Prototype With HTML5

Click here to go directly to the end of this article and play the game.

Creating The Game

We are using the Basic HTML5 Game Framework from a previous tutorial to quickly create this prototype. The game doesn’t have a lot of elements. It has a level which consists of 4 walls, a score and a bouncing ball. The only user-interaction that we need to make is detecting clicks or touch actions which results in showing the bottom wall.

We start by defining the level, the ball and a couple of variables. The level, a square room, has a position and a size. It also defines the thickness of the walls using wallsize. We will use this value later on when we are drawing the walls and when checking for collisions.

1
2
3
4
5
6
7
8
    // Level properties
    var level = {
        x: 80,
        y: 48,
        width: canvas.width - 160,
        height: canvas.height - 96,
        wallsize: 16
    };

The ball has a position and a size. In our code, the ball is actually a square. This helps us with collision detection. Of course, when we draw the ball, we draw the shape of a circle. The ball also needs a direction and a speed, because the ball is moving somewhere inside the level. The direction has two components, xdir and ydir. At each animation frame, these values are added to the position of the ball as we will see.

1
2
3
4
5
6
7
8
9
10
    // Ball
    var ball = {
        x: 0,
        y: 0,
        width: 35,
        height: 35,
        xdir: 0,
        ydir: 0,
        speed: 0
    }

The following variables are used in our calculations and interactions. Read the comments to see what they stand for.

1
2
3
4
5
6
7
8
9
    // Variables
    var score = 0;              // Score
    var blocked = false;        // Bottom wall is active
    var blockedtime = 0;        // How long the wall has been active
    var blockedlength = 0.1;    // How long the wall will be active
    var blockcooldown = 1;      // Waiting time between wall activations
    var gameover = true;        // Game is over
    var gameovertime = 0;       // How long we have been game over
    var gameoverdelay = 1;      // Waiting time after game over

Starting A New Game

When we start a new game, we need to initialize our variables to a default state. We initialize the ball by generating a random direction within certain limits by using the Math.random() function. You could say a direction vector is formed by combining the xdir and ydir variables of the ball. We want the direction vector to have a length of exactly 1, so we need to normalize this vector by calculating the length of the vector and dividing xdir and ydir by this length.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    function newGame() {
        // Initialize the ball
        ball.x = level.x + (level.width - ball.width) / 2;
        ball.y = level.y + level.height - ball.height;
        ball.speed = 500;
 
        // Random direction
        ball.xdir = 0.4 + (Math.random())/2;
        ball.ydir = -1;
 
        // Random left or right
        if (Math.random() < 0.5) {
            ball.xdir *= -1;
        }
 
        // Normalize the direction
        var dirlen = Math.sqrt(ball.xdir*ball.xdir + ball.ydir*ball.ydir);
        ball.xdir /= dirlen;
        ball.ydir /= dirlen;
 
        // Initialize the score
        score = 0;
 
        // Initialize variables
        blocked = false;
        blockedtime = blockcooldown;
        gameover = true;
        gameovertime = 0;
    }

Game Logic

The code below is the main game logic. What follows is a short overview of the code. The ball is moved in the direction of the xdir and ydir as we have seen before, with a certain speed. Next, if the wall is closed by the player, it needs to be opened after a certain time. Collisions are checked between the 3 static walls of the level and the ball. Finally, collisions between the closed bottom wall and the ball are checked. After every collision, the direction of the ball is reversed. If the ball escaped the level, the game is over. Check out the comments in the code below for more information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    function updateGame(dt) {
        // Move the ball, time-based
        ball.x += dt * ball.speed * ball.xdir;
        ball.y += dt * ball.speed * ball.ydir;
 
        // Open the wall after some time
        blockedtime += dt;
        if (blocked) {
            if (blockedtime > blockedlength) {
                blocked = false;
            }
        }
 
        // Handle left and right collisions with the level
        if (ball.x <= level.x) {
            // Left edge
            ball.xdir *= -1;
            ball.x = level.x;
        } else if (ball.x + ball.width >= level.x + level.width) {
            // Right edge
            ball.xdir *= -1;
            ball.x = level.x + level.width - ball.width;
        }
 
        // Handle top collisions with the level
        if (ball.y <= level.y) {
            // Top edge
            ball.ydir *= -1;
            ball.y = level.y;
        }
 
        // Check if the ball collides with the bottom wall
        // Only do this, while the ball is moving to the bottom
        if (blocked && ball.ydir > 0 &&
            rectIntersection(ball.x, ball.y, ball.width, ball.height,
                             level.x, level.y+level.height, level.width,
                             level.wallsize) && ball.ydir > 0) {
 
            // Wall is closed
            ball.ydir *= -1;
 
            // Increase the score
            score += 1;
 
            // Increase the speed of the ball by 5 percent
            ball.speed *= 1.05;
        } else if (ball.ydir > 0 && ball.y > level.y+level.height+level.wallsize) {
            // Ball escaped, game over
            ball.speed = 0;
            gameover = true;
        }
    }
 
    // Check if there is a rect intersection
    function rectIntersection(x1, y1, w1, h1, x2, y2, w2, h2) {
        if (x1 <= x2 + w2 && x1 + w1 >= x2 &&
            y1 <= y2 + h2 && y1 + h1 >= y2) {
 
            return true;
        }
 
        return false;
    }

This is our user interaction logic. When the player clicks the mouse button, we show the wall for some time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    // Mouse event handlers
    function onMouseDown(e) {
        // Get the mouse position
        var pos = getMousePos(canvas, e);
 
        // Start a new game
        if (gameover && gameovertime > gameoverdelay) {
            newGame();
            gameover = false;
        }
 
        // Show the wall
        if (!gameover && blockedtime >= blockcooldown) {
            blockedtime = 0;
            blocked = true;
        }
    }

Drawing The Game

The previous chapter showed the game logic. Now we have to present the game to the player by drawing all of the elements to the screen. We need to draw the walls, the score and the ball to the screen. For this protoype, we are simply using the HTML5 Canvas fillRect function to draw walls and we use the arc function to draw the circle of the ball. To draw the score, we use the canvas text drawing function fillText.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    // Render the game
    function render() {
        // Draw background
        context.fillStyle = "#577ddb";
        context.fillRect(0, 0, canvas.width, canvas.height);
 
        // Draw walls
        context.fillStyle = "#ffffff";
        context.fillRect(level.x-level.wallsize, level.y-level.wallsize, level.wallsize,
                         level.height+2*level.wallsize); // Left
        context.fillRect(level.x+level.width, level.y-level.wallsize, level.wallsize,
                         level.height+2*level.wallsize); // Right
        context.fillRect(level.x, level.y-level.wallsize, level.width, level.wallsize); // Top
 
        // Draw bottom wall if closed
        if (blocked) {
            context.fillRect(level.x, level.y+level.height, level.width, level.wallsize);
        }
 
        // Draw score inside the level
        context.fillStyle = "rgba(255, 255, 255, 0.7)";
        context.font = "240px Verdana";
        drawCenterText(score, level.x, level.y+level.height-150, level.width);
 
        // Draw the ball
        var centerx = ball.x + ball.width/2;
        var centery = ball.y + ball.height/2;
        context.beginPath();
        context.arc(centerx, centery, ball.width/2-3, 0, 2*Math.PI, false);
        context.fillStyle = "#000000";
        context.fill();
        context.lineWidth = 5;
        context.strokeStyle = "#ffffff";
        context.stroke();
 
        // Game over
        if (gameover) {
            context.fillStyle = "rgba(0, 0, 0, 0.5)";
            context.fillRect(0, 0, canvas.width, canvas.height);
 
            context.fillStyle = "#ffffff";
            context.font = "24px Verdana";
            drawCenterText("Click to start", 0, canvas.height/2, canvas.width);
        }
    }
 
    // Draw text that is centered
    function drawCenterText(text, x, y, width) {
        var textdim = context.measureText(text);
        context.fillText(text, x + (width-textdim.width)/2, y);
    }

Block It Prototype Example

Here is our finished prototype of a game like Block It. Click the screen to start the game. Don’t let the ball escape the room, by clicking the screen at the correct moment to make the bottom wall appear. The full source code is available on GitHub, licensed under GPLv3.