Friday, September 10, 2010

Tutorial: Simple game with HTML5 Canvas - part 1

Check other language versions: [RUSSIAN]


Tutorial: Simple game with HTML5 Canvas
Part 1 - Introduction & Background
Part 2 - Character & Animation
Part 3 - Physics & Controls
Part 4 - Platforms & Collisions
Part 5 - Scrolling & Game States

If you are interested in news and updates of that tutorial just follow me on twitter: [MichalBe's Twitter]

Because 10KApart is closed now, and we are all waiting for the results, it is good time to remind my very simple "Stairs to heaven" game and explain in details how it was made.

INTRODUCTION
StH is very simple clone of Doodle Jump, but to be honest I was inspired by Icy Tower and discover DJ after I submit StH to the competition. Never mind.
The goal is to control little angel & jump on the two kinds of platforms - orange (regular ones) and green (super high jump springboards). The game ends when the angel falls down to the bottom of the screen. Try it: [Stairs to heaven].
I create that game in about 8hours and later, after playing more and more, I discover few bugs so in this tutorial I want to fix it all. Let's do it!

Part 1. BACKGROUND
Because whole game, including images and scripts, couldn't be over 10K, I didn't want to use image on the background. It was cheaper to draw some generic-like stuff using canvas drawing functions.
First of all we need little HTML, nothing special, just one canvas element with some unique id, little bit of CSS and include of not existing yet game.js:
<html>
  <head>
    <title>Simple game with HTML5 Canvas</title>
  <style>
  body {
    margin:0px;
    padding:0px;
    text-align:center;
  }

  canvas{
    outline:0;
    border:1px solid #000;
    margin-left: auto;
    margin-right: auto;
  }
  </style>
  </head>
  <body>
    <canvas id='c'></canvas>
    <script src="game.js"></script>
  </body>
</html>
That's all in HTML we will need during this tutorial.
Ok, so let's create some Javascript.
First of all we need to create few global (for now, I know that global = evil) variables & change canvas attributes. That will be enough:
var width = 320,
//width of the canvas
  height = 500,
//height of the canvas

  c = document.getElementById('c'), 
//canvas itself 

  ctx = c.getContext('2d');
//and two-dimensional graphic context of the
//canvas, the only one supported by all 
//browsers for now

c.width = width;
c.height = height;
//setting canvas size 
First of all its important to understand one thing about canvas - it is not possible to move objects in the canvas surface. It's necessarily to clear it, whole or in the parts, on each frame. To achieve this, let's create clear() function.
var clear = function(){
  ctx.fillStyle = '#d0e7f9';
//set active color to #d0e... (nice blue)
//UPDATE - as 'Ped7g' noticed - using clearRect() in here is useless, we cover whole surface of the canvas with blue rectangle two lines below. I just forget to remove that line
//ctx.clearRect(0, 0, width, height);
//clear whole surface
  ctx.beginPath();
//start drawing
  ctx.rect(0, 0, width, height);
//draw rectangle from point (0, 0) to
//(width, height) covering whole canvas
  ctx.closePath();
//end drawing
  ctx.fill();
//fill rectangle with active
//color selected before
}
One colored clear background is boring as hell, so let's draw some clouds on it. Maybe not regular clouds, but simple, semitransparent circles imitating clouds. First we will draw some in random places of the canvas, each with different size and transparency. We will keep all the informations about circles in 2d array (there are no two-dimensional arrays in JS, best way to solve this is just put one Array into another).
var howManyCircles = 10, circles = [];

for (var i = 0; i < howManyCircles; i++) 
  circles.push([Math.random() * width, Math.random() * height, Math.random() * 100, Math.random() / 2]);
//add information about circles into
//the 'circles' Array. It is x & y positions, 
//radius from 0-100 and transparency 
//from 0-0.5 (0 is invisible, 1 no transparency)

var DrawCircles = function(){
  for (var i = 0; i < howManyCircles; i++) {
    ctx.fillStyle = 'rgba(255, 255, 255, ' + circles[i][3] + ')';
//white color with transparency in rgba
    ctx.beginPath();
    ctx.arc(circles[i][0], circles[i][1], circles[i][2], 0, Math.PI * 2, true);
//arc(x, y, radius, startAngle, endAngle, anticlockwise)
//circle has always PI*2 end angle
    ctx.closePath();
    ctx.fill();
  }
};
Nice, but boring less only a little. Why are the clouds standing still? Lets make a tiny little function with one Number type argument, which moves clouds down given number of pixels, and when particular circle disappears under the canvas, it will moves it on the top with changed position X, radius and transparency:
var MoveCircles = function(deltaY){
  for (var i = 0; i < howManyCircles; i++) {
    if (circles[i][1] - circles[i][2] > height) {
//the circle is under the screen so we change
//informations about it 
      circles[i][0] = Math.random() * width;
      circles[i][2] = Math.random() * 100;
      circles[i][1] = 0 - circles[i][2];
      circles[i][3] = Math.random() / 2;
    } else {
//move circle deltaY pixels down
      circles[i][1] += deltaY;
    }
  }
};
Now, last but not least, let's create main game loop and connect everything we create for now in there. Each frame will clear the screen, move circles 5px lower, draw them and after 1/50sec call another frame. I use two setTimeouts except one setInterval, but I'm not pretty sure why:). I know that there was some performance issues in IE back in the days or something. Also don't forget to add gLoop variable to that declared at the beginning.
var width = 320,  
//width of the canvas  
  height = 500,  
//height of the canvas  
  gLoop,
(...) //rest of the code goes here

var GameLoop = function(){
  clear();
  MoveCircles(5);
  DrawCircles();
  gLoop = setTimeout(GameLoop, 1000 / 50);
}
GameLoop();
According to Luis Giribone's comment below, I avoid Intervals and use Timeouts instead intentionally - Interval is called every 1000/fps seconds - even if the previous one disn't not finished yet. If you use Timeout, it will call another one only after previous was finished. I hope it is clear now. I also want to thanks Ped7g, author of Whiskas & Pedigree Javascript ad game for catching mistakes. Final result of that part should looks like this: [Simple game with HTML5 Canvas part 1], and sources are available on my Github account: [MichalBe] Tutorial: Simple game with HTML5 Canvas Part 1 - Introduction & Background Part 2 - Character & Animation Part 3 - Physics & Controls Part 4 - Platforms & Collisions Part 5 - Scrolling & Game States

61 comments:

  1. You are the man! Thanks for posting this great series!

    ReplyDelete
  2. Excellent tutorial! I am doing game-coding tutorials for some 10 years by now, and it's one of the most thorough, and well written! Many Thanks!

    P.S. Post Part 5 (The Scroll) - I beg You!

    ReplyDelete
  3. Great article, thanks for posting this! I've definitely learned quite a bit, and I plan to follow along.

    Thanks again!

    ReplyDelete
  4. This is just great, really like how you broke it down. I'll be having a go myself for sure!

    ReplyDelete
  5. why do you do "ctx.clearRect(0, 0, width, height);" to clear whole surface, when you fill up whole by blue just after that (i.e. clearing it twice effectively)

    Does it solve some browser quirk? (tried to patch it here for ff3.6, works OK without the clearRect)

    ReplyDelete
  6. ck ck ck...
    can be used as background ... awesome
    thanks for tutor^^
    salam from si bloglang anu ganteng kalem tea

    ReplyDelete
  7. congrats! keep up the good work/this is a great presentation.

    canvas Print

    ReplyDelete
  8. Awesome tuts! Thanks for posting.

    Just wondering, is there any specific reasoning behind you placing your functions within variables?

    I find it easier just to write them as functions, but I'm n00by so I don't know if there's logic behind it.

    ReplyDelete
  9. Thanks, nice to hear someone likes it:). You can find useful information about declaring functions for example in here: http://zazaq.com/2010/08/07/javascript-function-declaration-vs-function-expression/

    ReplyDelete
  10. i made only a simple change to avoid setTimeouts
    // game init obj
    var GameLoop = function(){
    clear();
    moveCircles(5);
    drawCircles();
    }
    //end of code init the game
    gLoop = setInterval( GameLoop, 1000/fps );
    so i don't set a timeout every GameLoop
    and is the same code to clear the interval.
    Thanks for the tutorial, great game!

    ReplyDelete
  11. Avoiding Intervals and using Timeouts instead was my point in here - Interval is called every 1000/fps seconds - even if the previous one did not finished yet. If you use Timeout, it will call another one only after previous was finished.

    ReplyDelete
  12. wow, hard to see that point,
    thanks for the clarification :)

    ReplyDelete
  13. you're welcome, that are tutorials for:)

    ReplyDelete
  14. It doesn't do anything. I copy and pasted it and it didn't work. it just shows a blank box that is the wrong size in my chrome tab.

    ReplyDelete
  15. So you are doing something in the wrong way. Try the code from repo.

    ReplyDelete
  16. How can I make the circles move on the x axis instead? :)

    ReplyDelete
  17. Thanks. I got it working now. Now I'm just trying to understand some of the code that I wrote here and why it works the way it does.

    ReplyDelete
  18. On the circles.push([]) why does it have the [] inside the () of the push?

    ReplyDelete
  19. How do we make the clouds go different speeds between like 1 and 10?

    ReplyDelete
  20. I mean how do you make each cloud have it's own individual speed? Like one like would go 5px the other would go 10px for example.

    ReplyDelete
  21. the answer is still the same - each cloud should have its own deltaY

    ReplyDelete
  22. I see. This code thing is sooooo logical.

    ReplyDelete
  23. Excellent tutorial! Everything works and it's perfect for a beginner with Canvas/JS.

    One question - is there any reason you put comments under your statements? It was just slightly difficult to read for me, that's all.

    ReplyDelete
  24. It's a useful tutorial!!!
    But just want to ask what is the usage for the game.js?You didn't mention it.

    ReplyDelete
  25. You have to put all the javaScript code inside this file.

    ReplyDelete
  26. Oh I see, thanks!!
    But I just see the source code in your example, all the javaScript code put in the HTML file but not the game.js?

    ReplyDelete
  27. Check github:
    https://github.com/michalbe/Simple-game-with-HTML5-Canvas
    everything is in .js file.

    ReplyDelete
  28. Thanx man. First for your great tutorial, second for jsbin :).

    ReplyDelete
  29. im pretty sure i did everything right, but i only get a blue screen and no clouds. Any suggestions?

    ReplyDelete
    Replies
    1. Same here. I dont see any cloud either.

      Delete
  30. Why do you make functions using var and not
    function funcname()?

    ReplyDelete
  31. Thanks for this great introduction to game development using HTML5 canvas, I managed to create this game after reading your post ! :)

    http://ole.im/tetris

    ReplyDelete
  32. Great tutorials thanks html 5 games coding so simple than flash games

    ReplyDelete
  33. Thanks for your tutorial. I learned lots from here like a e-book. Keep up your work.

    ReplyDelete
  34. Wonderful training cheers html page Five video games code consequently straightforward as compared to thumb games.

    ReplyDelete
  35. Thanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.

    ReplyDelete
  36. Thanks for the great tutorial. I really think that HTML5 will prove to be the future of online games and am excited to see what all the developers there will do with the technology.

    ReplyDelete
  37. This comment has been removed by the author.

    ReplyDelete
  38. This is a great tutorial.

    For the ones interested in this subject, I've created two premium courses in Udemy:

    http://www.udemy.com/html5-game-development

    http://www.udemy.com/create-a-html5-game-from-scratch/

    ReplyDelete
    Replies
    1. yes this is a great tutorial. and thanks FariAzz for link, this is a helpfull. thanks

      Delete
  39. HTML5 design provides such growth that allows better development of film and songs. It is also going to allow better system among the websites

    ReplyDelete
  40. I may be missing something because it is not working for me, any idea=?

    ReplyDelete
  41. Thanks for this awesome tutorial, I just finished this first step without any issues, it was also well explained :)

    ReplyDelete
  42. Thanks for sharing.
    I just finished this game.
    This's game: ungdungdohoa.com/demo/rabbit

    ReplyDelete
  43. another good tutorial

    http://www.binarytides.com/make-html5-game-box2d-javascript-tutorial/

    ReplyDelete
  44. I had a goal to learn how to make a game for web in one night. You sir have made that possible, thank you ^.^

    ReplyDelete
  45. My code isn't working. I just see a blue screen, but no clouds. Please help. I even copied the code, but it's not working.

    ReplyDelete
    Replies
    1. And how do you think I can help you without the code?

      Delete
    2. This was the only way I could get the clouds to work and stuff. But it's only placing one cloud at a time. I had to change a couple of things to get it to work, but the forloop for Draw Circles isn't working ('cause I should see at least 10 circles, I think, at a time).

      window.onload = GameLoop;

      var width = 320
      var height = 500

      var c = document.getElementById('c');
      var ctx = c.getContext('2d');

      function clear() {
      ctx.fillStyle = "#5A89CF";
      ctx.beginPath();
      ctx.fillRect(0, 0, 320, 500);
      ctx.closePath();
      }

      var howManyCircles = 10, circles = [Math.random() * width, Math.random() * height, Math.random() * 100, Math.random() / 2];

      function DrawCircles(){
      for (var i = 0; i < howManyCircles; i++){
      ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
      ctx.beginPath();
      ctx.arc(circles[0], circles[1], circles[2], 0, Math.PI * 2, true);
      // arc(x, y, radius, startAngle, endAngle(full circle), anticlockwise(true);
      ctx.closePath();
      ctx.fill();
      }
      };

      function MoveCircles(deltaY){
      for (var i = 0; i < howManyCircles; i++) {
      if(circles[1] - circles[2] > height){
      circles[0] = Math.random() * width;
      circles[2] = Math.random() * 100;
      circles [1] = 0 - circles[2];
      circles[3] = Math.random() / 2;
      } else {
      circles[1] += deltaY;
      }
      }
      };

      function GameLoop(){
      clear();
      MoveCircles(1);
      DrawCircles();
      gLoop = setTimeout(GameLoop, 1000/50);
      }

      Delete
  46. Uche, did you manage to get everything working?

    ReplyDelete
  47. HI, lovely tutorial, Anybody know how to get this game controls to work on a mobile device

    I've changed the onmousemove to ontouchmove but it only moves the character one way, and not the other any ideas?

    thanks

    ReplyDelete
    Replies
    1. Hey,
      It should work without any modifications.
      Cheers.

      Delete
    2. thanks for reply, sorry I should have been more clear, Im using cocoonJS to build this game as native on mobile which works brilliantly! but its the controls that don't work :[

      Delete
  48. hey, thank for great toturial, maybey you want create ebook or pdf with this tots? i want to create mmo rpg free game and this will be good start an motivation ;)

    ReplyDelete
  49. So where exactly would you put the javascript, after you put the tag or inside the html coding

    ReplyDelete
  50. Awesome, simply awesome!
    Thanks for the post, I have learned few new things from i html5

    ReplyDelete
  51. We Hold our Tutors to a Higher Standard, and Pay our Tutors More Fairly, than nearly any other Tutoring Company.Thus, we have Better (and more Experienced) Tutors.We accept only the top tutor applicants, and will never compromise our selectivity in order to artificially inflate our ranks. In addition, every member of the Team is required to have recently scored in the 95th percentile or above on any standardized test that he/she teaches.Math Tutor Austin, Math Tutor Atlanta, Math Tutor Houston, Math Tutor Seattle.
    Science Tutor Las Vegas | Math Tutor Hawaii | Math Tutor Las Vegas | Tutor for kids Las Vegas | Math Tutor Charlotte | Math Tutor Phoenix | Math Tutor Los Angeles | Math Tutor Boston | Math Tutor New York | Math Tutor Denver | Math Tutor Chicago |

    ReplyDelete
  52. Your post is really very nice and useful for everyone business for sale uk birmingham

    ReplyDelete