HTML5 Game Development – Lesson 7

HTML5 Game Development – Lesson 7

13 137915
HTML5 Game Development - Lesson 7

HTML5 Game Development – Lesson 7

Today we will create our first complete game – Arkanoid. In this lesson I will show you how to detect basic collisions and work with HTML5 local storage. You can operate with pad using mouse and keyboard (left/right buttons). We will store in local storage elapsed time of previous game and amount of broken bricks (points).

Here you can read our previous lesson: Developing Your First HTML5 Game – Lesson 6.

Here are our demo and downloadable package:

Live Demo
download in package

Ok, download the example files and lets start coding !


Step 1. HTML

Here is source code of our seventh lesson

index.html

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <title>HTML5 Game Development - Lesson 7 | Script Tutorials</title>
        <link href="css/main.css" rel="stylesheet" type="text/css" />
        <script src="js/jquery-1.5.2.min.js"></script>
        <script src="js/script.js"></script>
    </head>
    <body>
        <header>
            <h2>HTML5 Game Development - Lesson 7</h2>
            <a href="https://www.script-tutorials.com/html5-game-development-lesson-7/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
        </header>
        <div class="container">
            <canvas id="scene" width="800" height="600"></canvas>
        </div>
    </body>
</html>

Step 2. CSS

Here are used CSS styles.

css/main.css

/* page layout styles */
*{
    margin:0;
    padding:0;
}
body {
    background-color:#eee;
    color:#fff;
    font:14px/1.3 Arial,sans-serif;
}
header {
    background-color:#212121;
    box-shadow: 0 -1px 2px #111111;
    display:block;
    height:70px;
    position:relative;
    width:100%;
    z-index:100;
}
header h2{
    font-size:22px;
    font-weight:normal;
    left:50%;
    margin-left:-400px;
    padding:22px 0;
    position:absolute;
    width:540px;
}
header a.stuts,a.stuts:visited{
    border:none;
    text-decoration:none;
    color:#fcfcfc;
    font-size:14px;
    left:50%;
    line-height:31px;
    margin:23px 0 0 110px;
    position:absolute;
    top:0;
}
header .stuts span {
    font-size:22px;
    font-weight:bold;
    margin-left:5px;
}
.container {
    margin: 20px auto;
    overflow: hidden;
    position: relative;
    width: 800px;
}

Step 3. JS

js/jquery-1.5.2.min.js

We use jQuery for our lesson. Available in package. Next file most important (here are all our html5 functional):

js/script.js

// inner variables
var canvas, ctx;
var iStart = 0;
var bRightBut = false;
var bLeftBut = false;
var oBall, oPadd, oBricks;
var aSounds = [];
var iPoints = 0;
var iGameTimer;
var iElapsed = iMin = iSec = 0;
var sLastTime, sLastPoints;
// objects :
function Ball(x, y, dx, dy, r) {
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.r = r;
}
function Padd(x, w, h, img) {
    this.x = x;
    this.w = w;
    this.h = h;
    this.img = img;
}
function Bricks(w, h, r, c, p) {
    this.w = w;
    this.h = h;
    this.r = r; // rows
    this.c = c; // cols
    this.p = p; // padd
    this.objs;
    this.colors = ['#9d9d9d', '#f80207', '#feff01', '#0072ff', '#fc01fc', '#03fe03']; // colors for rows
}
// -------------------------------------------------------------
// draw functions :
function clear() { // clear canvas function
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    // fill background
    ctx.fillStyle = '#111';
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}
function drawScene() { // main drawScene function
    clear(); // clear canvas
    // draw Ball (circle)
    ctx.fillStyle = '#f66';
    ctx.beginPath();
    ctx.arc(oBall.x, oBall.y, oBall.r, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
    if (bRightBut)
        oPadd.x += 5;
    else if (bLeftBut)
        oPadd.x -= 5;
    // draw Padd (rectangle)
    ctx.drawImage(oPadd.img, oPadd.x, ctx.canvas.height - oPadd.h);
    // draw bricks (from array of its objects)
    for (i=0; i < oBricks.r; i++) {
        ctx.fillStyle = oBricks.colors[i];
        for (j=0; j < oBricks.c; j++) {
            if (oBricks.objs[i][j] == 1) {
                ctx.beginPath();
                ctx.rect((j * (oBricks.w + oBricks.p)) + oBricks.p, (i * (oBricks.h + oBricks.p)) + oBricks.p, oBricks.w, oBricks.h);
                ctx.closePath();
                ctx.fill();
            }
        }
    }
    // collision detection
    iRowH = oBricks.h + oBricks.p;
    iRow = Math.floor(oBall.y / iRowH);
    iCol = Math.floor(oBall.x / (oBricks.w + oBricks.p));
    // mark brick as broken (empty) and reverse brick
    if (oBall.y < oBricks.r * iRowH && iRow >= 0 && iCol >= 0 && oBricks.objs[iRow][iCol] == 1) {
        oBricks.objs[iRow][iCol] = 0;
        oBall.dy = -oBall.dy;
        iPoints++;
        aSounds[0].play(); // play sound
    }
    // reverse X position of ball
    if (oBall.x + oBall.dx + oBall.r > ctx.canvas.width || oBall.x + oBall.dx - oBall.r < 0) {
        oBall.dx = -oBall.dx;
    }
    if (oBall.y + oBall.dy - oBall.r < 0) {
        oBall.dy = -oBall.dy;
    } else if (oBall.y + oBall.dy + oBall.r > ctx.canvas.height - oPadd.h) {
        if (oBall.x > oPadd.x && oBall.x < oPadd.x + oPadd.w) {
            oBall.dx = 10 * ((oBall.x-(oPadd.x+oPadd.w/2))/oPadd.w);
            oBall.dy = -oBall.dy;
            aSounds[2].play(); // play sound
        }
        else if (oBall.y + oBall.dy + oBall.r > ctx.canvas.height) {
            clearInterval(iStart);
            clearInterval(iGameTimer);
            // HTML5 Local storage - save values
            localStorage.setItem('last-time', iMin + ':' + iSec);
            localStorage.setItem('last-points', iPoints);
            aSounds[1].play(); // play sound
        }
    }
    oBall.x += oBall.dx;
    oBall.y += oBall.dy;
    ctx.font = '16px Verdana';
    ctx.fillStyle = '#fff';
    iMin = Math.floor(iElapsed / 60);
    iSec = iElapsed % 60;
    if (iMin < 10) iMin = "0" + iMin;
    if (iSec < 10) iSec = "0" + iSec;
    ctx.fillText('Time: ' + iMin + ':' + iSec, 600, 520);
    ctx.fillText('Points: ' + iPoints, 600, 550);
    if (sLastTime != null && sLastPoints != null) {
        ctx.fillText('Last Time: ' + sLastTime, 600, 460);
        ctx.fillText('Last Points: ' + sLastPoints, 600, 490);
    }
}
// initialization
$(function(){
    canvas = document.getElementById('scene');
    ctx = canvas.getContext('2d');
    var width = canvas.width;
    var height = canvas.height;
    var padImg = new Image();
    padImg.src = 'images/padd.png';
    padImg.onload = function() {};
    oBall = new Ball(width / 2, 550, 0.5, -5, 10); // new ball object
    oPadd = new Padd(width / 2, 120, 20, padImg); // new padd object
    oBricks = new Bricks((width / 8) - 1, 20, 6, 8, 2); // new bricks object
    oBricks.objs = new Array(oBricks.r); // fill-in bricks
    for (i=0; i < oBricks.r; i++) {
        oBricks.objs[i] = new Array(oBricks.c);
        for (j=0; j < oBricks.c; j++) {
            oBricks.objs[i][j] = 1;
        }
    }
    aSounds[0] = new Audio('media/snd1.wav');
    aSounds[0].volume = 0.9;
    aSounds[1] = new Audio('media/snd2.wav');
    aSounds[1].volume = 0.9;
    aSounds[2] = new Audio('media/snd3.wav');
    aSounds[2].volume = 0.9;
    iStart = setInterval(drawScene, 10); // loop drawScene
    iGameTimer = setInterval(countTimer, 1000); // inner game timer
    // HTML5 Local storage - get values
    sLastTime = localStorage.getItem('last-time');
    sLastPoints = localStorage.getItem('last-points');
    $(window).keydown(function(event){ // keyboard-down alerts
        switch (event.keyCode) {
            case 37: // 'Left' key
                bLeftBut = true;
                break;
            case 39: // 'Right' key
                bRightBut = true;
                break;
        }
    });
    $(window).keyup(function(event){ // keyboard-up alerts
        switch (event.keyCode) {
            case 37: // 'Left' key
                bLeftBut = false;
                break;
            case 39: // 'Right' key
                bRightBut = false;
                break;
        }
    });
    var iCanvX1 = $(canvas).offset().left;
    var iCanvX2 = iCanvX1 + width;
    $('#scene').mousemove(function(e) { // binding mousemove event
        if (e.pageX > iCanvX1 && e.pageX < iCanvX2) {
            oPadd.x = Math.max(e.pageX - iCanvX1 - (oPadd.w/2), 0);
            oPadd.x = Math.min(ctx.canvas.width - oPadd.w, oPadd.x);
        }
    });
});
function countTimer() {
    iElapsed++;
}

I added my comments anywhere, hope that all code is pretty understandable. Please pay attention to ‘localStorage’ object to understand how to work with HTML5 Local storage (I use ‘setItem’ method to store something and ‘getItem’ to read this from local storage). Also, it can be also interesting to understand how to calculate collisions between the ball and bricks.


Live Demo
download in package

Conclusion

Today, we made our first arkanoid game. Most functionality are already here. We have implemented our first knowledge about collisions and HTML5 local storage too. I will be glad to see your thanks and comments. Good luck!

SIMILAR ARTICLES


13 COMMENTS

  1. on my foirfex 6.0.1 it works fine! :-DI wanted to make an animated background for my blog, but maybe it’s too early. :-D

  2. Its a great tutorial. But i think you have missed out a major part of the game… Why isn’t there a Game over message popping up when the game is over ?

  3. great tutorial! how could I add the messages? for example: when the ball hits a ground?

    many thanx and all the best for new year!

  4. Is it possible to remove the points and time? I’ve been trying to to understand where they are implemented.

  5. Amazing tutorail. I just wnated to ask why it doesnt work without js/jquery-1.5.2.min.js in thaht example.

Leave a Reply