HTML5 Zone is brought to you in partnership with:

I was born in 1981 in one little city. Since I was 10y/o I programmed in different languages. My first languages were basic, then C++/MFC, after .Net (C#, VB.Net, J#, ASP.Net), XSL+XML processing). In the last 5 years I worked with web languages (HTML, CSS, PHP, SQL, XML, XSL, JavaScript). After university I worked in several different companies, eventually becoming a blogger. This is my hobby too. Andrey is a DZone MVB and is not an employee of DZone and has posted 109 posts at DZone. You can read more from them at their website. View Full User Profile

HTML5 Game Development - Lesson 7

01.24.2012
| 3367 views |
  • submit to reddit

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 using mouse and keyboard (left/right buttons). We will store in local storage the elapsed time of previous games and the 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 packages:

Live Demo

download in package

Ok, download the example files and lets start coding !

Step 1. HTML

Here is the 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="http://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. This file is the most important (here all our html5 functionality is located):

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 / <img src="http://www.script-tutorials.com/wp-includes/images/smilies/icon_cool.gif" alt="8)" class="wp-smiley"> - 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!

 

Source: http://www.script-tutorials.com/html5-game-development-lesson-7/

Published at DZone with permission of Andrey Prikaznov, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)