Create cross platform HTML5 games – Level 1

Creating games is the most exciting and challenging area of the software development. Once you have created the next hit game of the year, the challenge for your newborn baby is to support as many different platforms as possible since there are thousands of different devices out there. Thus, unless your target is a specific device, like iPhone, Android, Smart watch, Windows PC, Mac, etc. with previously known technical characteristics, then the need of such a wide support could become a real headache.

responsive_spaceballAn approach to this is to re-write the source code (or specific parts of it) for every platform using the SDK and the tools for this platform.

Another approach is to write the game using technologies that come with the promise “write once, run everywhere”. One such very promising technology is HTML5. Orienting your coding habits into this direction, a cross-platform implementation becomes feasible.

In this tutorial I am going to show you how I have used HTML5 to write such a cros-platform game. The reader could easily be confused with the term“HTML5” believing that somehow it is possible to write such complex applications using HTML-tags (something like <game><img src=”….”, …, put some game logic here </game> and …that’s all. NO!).  In fact, HTML5 is 1% HTML and 99% JavaScript. The tutorial assumes that you already have a basic experience with JavaScript programming, or with …programming.

Actually, an HTML5 game (or application) runs inside a browser that supports this level of the markup language. Fortunately, almost every browser on any device of the last half decade can translate HTML5.

You can play a game that I have created with HTML5 and combines all these promises. Just visit from everywhere the link bellow. If you open it from a desktop browser please keep changing the dimensions to experience the effect of responsive resizing in real-time.

http://www.liknongames.com/spaceball

Now, let’s begin with the fun.

The magical thing in HTML5 is that this version of the language offers a new <canvas> HTML-element and from now on, dozens of built-in JavaScript functions can interact with it. Like in a canvas of the real world, JavaScript can draw on this element all primitive graphic shapes (lines, squares, circles, etc), set and change its dimensions, rotate it, color it, write text on it and of course draw preloaded images. An internal sound-library is also available. Touch events can be caught as usual.

What else does someone need to create a game? …Nothing else.

For the needs of this first tutorial I have created a simple version of the game above. The astronaut can be navigated left or right through two in-game buttons or using the arrow keys if a keyboard is presented. In this downgraded version, the game is resized (not in the smart way as in the full version) but not rotated according to the orientation of the device (like it happens with the full version).
http://liknongames.com/sf3n40nsb/html5tut1/spaceball

These advanced behavior will be covered in the next tutorial (Level 2).
The index.html  (http://liknongames.com/sf3n40nsb/html5tut1/spaceball/index.html) looks quite simple:

<!DOCTYPE html>
<html>
<head>
  <title>Spaceball</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
  <link href="scripts/style.css" rel="stylesheet" type="text/css">
  <link REL="SHORTCUT ICON" HREF="images/fav.ico">
</head>
<body>
  <div id="stage">
    <canvas id="canvas"></canvas>
  </div>
  <script src="scripts/game.js" type="text/javascript"></script>
</body>
</html>

It defines the <canvas> element and gives an id to it (id=”canvas”) so that it can be imported into the source code of the JavaScript program stored to the
http://liknongames.com/sf3n40nsb/html5tut1/spaceball/scripts/game.js
line 30:

// Canvas itself
var canvas = document.getElementById('canvas');

 

A style-sheets script is also paired with the HTML source
http://liknongames.com/sf3n40nsb/html5tut1/spaceball/scripts/style.css
that defines the background color (black) and disables the horizontal and vertical scrollbars of the browser.

Once the HTML-page is being loaded the ‘onload’ listener in our JavaScript program is triggered (line 465)

document.addEventListener('load', function ()...

There, the dimensions of the window are assigned to a pair of variables and the startGame(boolean) function (line 79) is executed with “true” as its single, boolean argument, that indicates that this is the very first time the page is loaded (and the game too) and thus all resources of the game need to be initialized and a thread-like function has to start the periodic call of itself  (runGame, line 577, re-called in line 604 with the JavaScript function setTimeout )

By calling startGame all values related with the size of the canvas are calculated. This take place inside the function initDimensions  (line 511). The meaning of the assignment there is that the canvas-object will be as big as the entire window of the browser (lines 513 + 514). This tutorial is about games that use the entire window of the browser.

canvas.width  = window.innerWidth;
canvas.height = window.innerHeight;

Right after this point a context is extracted from the canvas-object with the option to support 2D-operations, since the game is a 2D game. 3D is also supported in HTML5.
line 515:

context = canvas.getContext('2d');

And now comes one of the most significant variables in our coding- journey.  A multiplication-factor is calculated that keeps the value of how the scene has to be scaled so to fit to the browser’s window.  (line 517)

fScaleFactor = window.innerWidth/WIDTH_GRAPHICS_DESIGNED_FOR;

There, the predefined value “WIDTH_GRAPHICS_DESIGNED_FOR” tells us the width of the scene (in pixels), that the designer has drawn the graphics of the game for

After these actions have been completed, the initContext() function at line 112 applies the previous calculated multiplicator on the previously instantiated context of the canvas.
line 114:

context.transform ( fScaleFactor, 0, 0, fScaleFactor, 0, 0);

As already said, all resources must be loaded before the beginning of the repeatedly projection of graphic elements on the screen. One problem is that the built-in function of JavaScript that loads graphics from raw files is no-blocking. The programmer simply specifies the path and the name of the graphics file…

imgForButtons[0] = new Image(); (line 193) …
imgForButtons[0].src = "./images/btn_released.png";  (line 200)

…but that doesn’t guarantee that after this line the image is loaded.

For this reason, an ‘onload’ listener should be applied for single image so to specify the actions when finally the image is completely loaded. Since at any point of loading of resources it is known how many of them should be loaded, I increase a counter (iLoadedImages) every time an image is loaded and at time when the counter reaches the total amount of graphics (TOTAL_GRAPHICS_TO_LOAD) then and only then the initialization is indicated as complete and we can go to the next step.
line 194 to 199:

imgForButtons[0].onload = function()
{
     iLoadedImages++;
     if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
          initGameObjects(true);
}

Once the contition if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD ) is satisfied, the function initGameObjects(boolean)  creates all ….game-objects (specifically implemented for this game) that use the recently loaded resources. When passing ‘true’ as argument (which is the case in the very first loading of the game) the function creates the yet undefined instances of these game-objects (line 133) since they were not existed before. The other choice is to call it with ‘false’ as it is the case in the startGame when only the positions of the already, previously instatiated game-objects should be rearranged (line 145).

Now, everything is ready and and the runGame( ) function (line 577) gets into the interesting part (line 595)

...
else if ( GameState != STATE_LOAD_RESOURCES )
{
     handleUserInput();
     if ( GameState == STATE_GAME_STARTED )
     {
          calculate_positions();
     }
}

The function acts like the main game-loop since it is repeatedly executed by itself (line 604).
setTimeout(runGame, 10);// every 10 milliseconds

In every single cycle of this loop the user’s input is checked by calling the function          handleUserInput() (defined in line 316). It checks if the coordinates of a click on the screen are inside the two in-game buttons or if the left/right arrow-keys of a keyboard (if presented) has been pressed. The value of these coordinates is updated asynchronously through the listeners ‘mousedown’ (from a connected mouse, line 434),  ‘touchstart’ (for mobile devices, line 451) and ‘keydown’ (line 476).  Also there, the touch-coordinates are translated to the (perhaps) resized world of the game.

Back now to the runGame( )  function.

After handling the input of the user, the new positions are calculated by executing the function calculate_positions( ). This is where the logic of the game is implemented and the new positions for every actor or thing in the game is re-calculated. For the needs of this demo this function does absolute nothing, while the body of the same function in the full version of the game counts a few hundreds lines of source code.  It is completely up to you to code there your own logic about how your game will interact with the player.

At last, after this Odyssey we are ready to display the frame according to the newly calculated positions. This is performed at the end of our game-loop through the function onDrawFrame(), defined at line 526. There, the actual drawing on the screen take place.

This is possible by calling the draw() member-function of every game-object. For example,such objects are the graphics to the background, mentioned in line 600,

gameImageBgrGraphics[i].draw()

The class “GameObject” is defined in line 367. Actually, in JavaScript there isn’t any “class”-keyword. That what is known as a “class” can however be defined even in JavaScript as a “function”.

The GameObject-class specifies a structure that stores the image or the images used of a game-object, its coordinates and two member-functions. The first, draw(), is a wrapper to the built-in drawImage(…) function of JavaScript. The other, is used to recalculate the coordinates in case of changing the rotation of the device or when the browser’s window is resized (more about it in the next tutorial, Level 2).

Now, that I spoke about these two cases, it is time to reveal the big secret: Both of these cases are in fact the same one. How different is to change the orientation (of a smartphone) than changing the dimensions of the browser?  When a device is rotated then the new width of the browser is (almost) its previous height and similar with the new height.

And since I discovered that it was not safe to rely on the indicator of the device that it has changed its orientation, this is why at the line 579, I decided to check if the (eventually) new dimensions are different than these of the previous frame and if they are, then the startGame function is called but this time with a ‘false’ argument, so that only a re-position of the elements follows (line 145, initGameObjects). In this case also the dimensions of the canvas are re-initialized and the new value of the scale factor is re-calculated.

That’s all for this first part. Now, you have all information you need to look deeper into the code and get the knowledge from the source.



Theodosis Ekizoglou

Biography to be completed

More Posts - Website

Follow Me:
LinkedIn