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!
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
WOW! I have to leave something here, thats amazing! Thank you for sharing!
THANKS FOR TUTORIAL i LOVE STUFF YOU CAN DO WITH HTML5 AND JAVASCRIPT
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 ?
Hello Johnny,
Hehe, yes, you are right, I forgot about it
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!
Hi Barbara,
Please pay attention to code below line 108
Thanks for the tutorial, I like it.
thank you for the tutorial..
Is it possible to remove the points and time? I’ve been trying to to understand where they are implemented.
Hello Rika,
Yes, you can hide it, refer to the 123 – 135 lines of the JS
Amazing tutorail. I just wnated to ask why it doesnt work without js/jquery-1.5.2.min.js in thaht example.
Hi Jake,
Because jQuery is used in the ‘script.js’