Thursday, March 3, 2016

Create cross-platform HTML5 games - Episode I

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.



An 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 magic 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 (Episode II). 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. 
lines 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, Episode II). 

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.
"use strict";

var LEFTRIGHT_BTN_Y  = 340; // The y-axis position of the bottom buttons
var LEFT_BTN_X       =  30; // The x-axis position of the buttons at the left

var WIDTH_GRAPHICS_DESIGNED_FOR  = 560;
var HEIGHT_GRAPHICS_DESIGNED_FOR = 420;

var PORTRAIT                     = 0;
var LANDSCAPE                    = 1;

var MODE_GAME_IS_DESIGNED_FOR    = (WIDTH_GRAPHICS_DESIGNED_FOR>HEIGHT_GRAPHICS_DESIGNED_FOR)?LANDSCAPE:PORTRAIT;

// Game states
var STATE_LOAD_RESOURCES         = 1;
var STATE_GAME_STARTED           = 2;

// Options for the clickable objects
var OPTION_TYPE_INSTANT          = 0;
var OPTION_TYPE_CONTINUOUS       = 1;
var OPTION_TYPE_PRESSED          = 2;

var POINTER_PRESSED              = 1;
var POINTER_RELEASED             = 2;

var canvasIsActivated            = false; // the canvas is activated once it is clicked for the first time
var timeOnCanvasActivation       = 0;
var showMessage                  = true;

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

// and two-dimensional graphics context of the canvas
var context;

var MAIN_BACKGROUNDS   = 1;
var STATIC_GRAPHICS    = 4;
var TOTAL_GRAPHICS_TO_LOAD = 4 + MAIN_BACKGROUNDS + STATIC_GRAPHICS ;

var GameState     = STATE_LOAD_RESOURCES;
var iLoadedImages = 0;

var touch_x = -1;
var touch_y = -1;
var pointerStates = POINTER_RELEASED;

// Image objects (standard JavaScript)
var imgBackgroundGraphics = [];
var imgPlayer = [];
var imgBgr;
var imgForButtons   = [];

// Game objects (this game)
var gameImageBgrGraphics  = [];
var gameImageMainBackground;
var gameImageRightBtn;
var gameImageLeftBtn;
var oPlayer;

// Options for the left/right buttons.
var optionRightButton;
var optionLeftButton;

var isLeftButtonClicked  = false;
var isRightButtonClicked = false;

// dimensions of the canvas
var previousInnerW = 0;
var previousInnerH = 0;

var fScaleFactor      = 0;


//
//   starts a new game or restarts it after a change in the viewport has occured
//             true: in the loading of the game so to load all resources
//             false: when only reposition is needed (in resizing or rotation)
// 
function startGame(isFirstLoading)
{
     initDimensions();
     initContext();
     if ( isFirstLoading == true )
     {
          runGame();
          LoadResources();
     }
     else
          initGameObjects(false); // only recalculate some properties of the used objects
}


// 
// clears the stage so to draw the next frame in an empty (white in this case) context
//
// 
var clear = function () 
{
     context.fillStyle = '#FFFFFF';
     context.beginPath();
     context.rect ( 0, 0, WIDTH_GRAPHICS_DESIGNED_FOR, HEIGHT_GRAPHICS_DESIGNED_FOR);
     context.closePath();
     context.fill();
};



//
// initialise the context by taking into consideration the scaling
//
//
function initContext()
{
     context.transform ( fScaleFactor, 0, 0, fScaleFactor, 0, 0);
}



function initGameObjects(isFirstLoading)
{
     // initial position of the player
     var PlayerX  = 378;
     var PlayerY  = 116;

     // itinial positions of the static graphics on the stage 
     var posOfBgrGraphics   = 
                    [[  1,10],  // u.f.o
                    [100,240],  // star
                    [304,64],   // mountain
                    [  7,254]]; // asteroid

     // if it is the first time the game is loaded, then create all objects
     if ( isFirstLoading == true)
     {
          oPlayer     = new GameObject( 2,PlayerX,PlayerY,0,imgPlayer);
          for ( var i = 0; i < STATIC_GRAPHICS; i++ )
               gameImageBgrGraphics[i] = new GameObject(1, posOfBgrGraphics[i][0], posOfBgrGraphics[i][1], 0, imgBackgroundGraphics[i] );   
          gameImageMainBackground = new GameObject ( 1, 0, 0, 0, imgBgr);
          gameImageLeftBtn  = new GameObject ( 2, LEFT_BTN_X, LEFTRIGHT_BTN_Y, 0, imgForButtons );
          gameImageRightBtn = new GameObject ( 2, WIDTH_GRAPHICS_DESIGNED_FOR-(LEFT_BTN_X + imgForButtons[0].width), LEFTRIGHT_BTN_Y, 0, imgForButtons);

          optionLeftButton     = new Option();
          optionRightButton    = new Option();
     }
     else // on resising the screen or on changed orientation, re-calculate the positions 
     {    // to the already existed game-objects.
          oPlayer.reposition();
          for ( var i = 0; i < STATIC_GRAPHICS; i++ )
               gameImageBgrGraphics[i].reposition();
          gameImageMainBackground.reposition();
          gameImageLeftBtn.reposition();
          gameImageRightBtn.reposition();
     }
     optionLeftButton.x       = gameImageLeftBtn.x;
     optionLeftButton.y       = gameImageLeftBtn.y;
     optionLeftButton.width   = gameImageLeftBtn.width[0];
     optionLeftButton.height  = gameImageLeftBtn.height[0];
     optionLeftButton.type    = OPTION_TYPE_CONTINUOUS;
     optionLeftButton.keyCode = 37;
     optionLeftButton.prevState = 0;
     
     optionRightButton.x       = gameImageRightBtn.x;
     optionRightButton.y       = gameImageRightBtn.y;
     optionRightButton.width   = gameImageRightBtn.width[0];
     optionRightButton.height  = gameImageRightBtn.height[0];
     optionRightButton.type    = OPTION_TYPE_CONTINUOUS;     
     optionRightButton.keyCode = 39;
     optionRightButton.prevState = 0;
     GameState = STATE_GAME_STARTED;
}


//
// asynchronous loading of the graphics 
//
function LoadResources()
{
     var sStatics = ["ufo","star","mountain","asteroid"];
     
     for ( var j = 0; j < STATIC_GRAPHICS; j++ )
     {
          imgBackgroundGraphics[j] = new Image();
          imgBackgroundGraphics[j].onload = function ()
          {
               iLoadedImages++;
               if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
                    initGameObjects(true);
          }
          imgBackgroundGraphics[j].src = "./images/" + sStatics[j] + ".png";
     }

     // graphic for the released-state of the buttons
     imgForButtons[0] = new Image();
     imgForButtons[0].onload = function()
     {
          iLoadedImages++;
          if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
               initGameObjects(true);
     }
     imgForButtons[0].src = "./images/btn_released.png";
     

     // graphic for the pressed-state of the buttons
     imgForButtons[1] = new Image();
     imgForButtons[1].onload = function()
     {
          iLoadedImages++;
          if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
               initGameObjects(true);
     }
     imgForButtons[1].src = "./images/btn_pressed.png";
     
     // The player
     for ( var i = 0; i < 2; i++)
     {
          imgPlayer[i] = new Image();
          imgPlayer[i].onload = function ()
          {
               iLoadedImages++;
               if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
                    initGameObjects(true);
          }
          imgPlayer[i].src = "./images/me" + i + ".png";
     }

     // MAIN_BACKGROUND(s)
     imgBgr = new Image();
     imgBgr.onload = function ()
     {
          iLoadedImages++;
          if ( iLoadedImages == TOTAL_GRAPHICS_TO_LOAD )
               initGameObjects(true);
     }
     imgBgr.src = "./images/bgr0.png";
}


// 
// returns true when the current position of the touch 
// has been done inside the option and false in the other case 
//
function hasTouchedOnOption ( option )
{
     if ( pointerStates == POINTER_RELEASED )
     {
          if ( option.type == OPTION_TYPE_PRESSED )
          {
               if ( option.prevState == 1)
               {
                    option.prevState = 0;
                    return true;
               }
          }
          option.prevState = 0;
          return false;
     }

     if ( option.type == OPTION_TYPE_INSTANT )
     {
          if ( option.prevState == 1 ) // it was pressed
          {
               return false;
          }
     }
     
     // touch has been performed out, at the right of the option
     // [OPTION]  [TOUCH]
     if ( touch_x > option.x + option.width  )
     {
          option.prevState = 0;
          return false;
     }

     // touch has been performed out, at the left of the option
     // [TOUCH]   [OPTION]
     if ( touch_x < option.x )
     {
          option.prevState = 0;
          return false;
     }

     // touch has been performed out, under the bottom of the option
     //  [OPTION]
     //
     // [TOUCH]
     if ( touch_y > option.y + option.height )
     {
          option.prevState = 0;
          return false;
     }

     // touch has been performed out, over the top of the option
     // [TOUCH]
     //
     //   [OPTION]
     if ( touch_y < option.y )
     {
          option.prevState = 0;
          return false;
     }

     option.prevState = 1;

     if ( option.type != OPTION_TYPE_PRESSED )
     {
          return true;
     }
     return false;
}    



//
//  performs the interaction of the user 
//
function handleUserInput ( ) 
{
     // ...just to make sure...
     if ( isNaN(touch_x) || isNaN(touch_y) )
     {
          touch_x = -1;
          touch_y = -1;
     }
     
     isLeftButtonClicked = isRightButtonClicked = false;

     isLeftButtonClicked  = hasTouchedOnOption(optionLeftButton);    
     if ( isLeftButtonClicked == false )
     {
          isRightButtonClicked = hasTouchedOnOption ( optionRightButton);
     }

     if ( GameState == STATE_GAME_STARTED )
     {
          if (  isLeftButtonClicked == true )
          {
               oPlayer.currentFrame = 0;
               oPlayer.x -= 1;
          }
          else if ( isRightButtonClicked == true )
          {
               oPlayer.currentFrame = 1;
               oPlayer.x += 1;
          }
     }
}


//
// 'structure' for clickable objects
//
function Option()
{
     this.x = -1;
     this.y = -1;
     this.keyCode = -1;
     this.width   = 0;
     this.height  = 0;
     this.type;
     this.prevState;
}


//
//   'class' for the special object of this game
//
function GameObject(Frames, x, y, startingFrame, img )
{
     this.x = x;
     this.y  = y;
     this.type = 0; // type of a clickable object
     this.bActive = false;
     this.currentFrame = 0;
     this.Frames = Frames;
     this.img    = img;
     this.width  = [];
     this.height = [];
     this.currentFrame = startingFrame;

     if ( Frames == 1 )
          this.img[0] = img; 
          
     for ( var i = 0; i < Frames; i++ )
     {
          this.width[i]  = this.img[i].width;
          this.height[i] = this.img[i].height;
     }

     this.draw = function()
     {
          context.drawImage ( this.img[this.currentFrame], 0, 0, 
               this.width[this.currentFrame], 
               this.height[this.currentFrame],
               this.x,
               this.y,
               this.width[this.currentFrame], 
               this.height[this.currentFrame] );
     }
     
     this.reposition = function()
     {
          this.y = y;
     }
}



document.onmouseup = function(e)
{
     // do 'release touch' actions
     onPoinerRelease()     
} 


// The equivalent for onmousedown on touchscreen devices is ontouchstart, 
// and the equivalent of onmouseup is ontouchend.
document.ontouchend = function(e)
{
     // do 'release touch' actions
     onPoinerRelease()     
}


function onPoinerRelease()
{
     pointerStates = POINTER_RELEASED;
     touch_x = -1; 
     touch_y = -1;
     isLeftButtonClicked = false;
     isRightButtonClicked = false;
}


document.addEventListener('mousedown', function (e) 
{
          if ( canvasIsActivated == false )
          {
               canvasIsActivated = true;
               timeOnCanvasActivation = new Date().getTime();
          }
          touch_x =  e.pageX/fScaleFactor;
          touch_y =  e.pageY/fScaleFactor;      
          pointerStates = POINTER_PRESSED;
});


// How to impliment a onmousedown and onmouseup on a touch screen iphone
// http://stackoverflow.com/questions/21009821/how-to-impliment-a-onmousedown-and-onmouseup-on-a-touch-screen-iphone
// The equivalent for onmousedown on touchscreen devices is ontouchstart, 
// and the equivalent of onmouseup is ontouchend.
document.addEventListener('touchstart', function (e) 
{
          if ( canvasIsActivated == false )
          {
               canvasIsActivated = true;
               timeOnCanvasActivation = new Date().getTime();
          }
          touch_x =  e.pageX/fScaleFactor;
          touch_y =  e.pageY/fScaleFactor;      
          pointerStates = POINTER_PRESSED;
});



document.addEventListener('load', function () 
{
     setTimeout(function () 
     {
          previousInnerW = window.innerWidth;
          previousInnerH = window.innerHeight;
          startGame(true);
     }, 1000)
}, true);


document.addEventListener('keydown', function(event) 
{
     pointerStates = POINTER_PRESSED;
     if ( event.keyCode == 37 )
     {
          touch_x = gameImageLeftBtn.x[0] + 5;
          touch_y = gameImageLeftBtn.y[0] + 5;
     }
     else if ( event.keyCode == 39 )
     {
          touch_x = gameImageRightBtn.x[0] + 5;
          touch_y = gameImageRightBtn.y[0] + 5;
     }
          
     if (event.preventDefault) { event.preventDefault(); }
     if (event.stopPropagation) { event.stopPropagation(); }
          return false;
});



document.addEventListener('keyup', function(event) 
{
     onPoinerRelease();
});



function GameOver()
{
     // do 'game over' actions
}



function initDimensions()
{
     canvas.width  = window.innerWidth; 
     canvas.height = window.innerHeight;
     context = canvas.getContext('2d');
     // calculate how finally was the scaling 
     fScaleFactor = window.innerWidth/WIDTH_GRAPHICS_DESIGNED_FOR;
}



// OnDrawFrame, performs the actual display on the screen of all graphic elements.
// It is called repeatedly from the main loop of the game.
// While the resources are still loaded, it displays the percent of the  
// loading procedure, using the embedded fonts (line 604) 
function onDrawFrame( )
{
     if ( GameState != STATE_LOAD_RESOURCES )
     {
          gameImageMainBackground.draw();
          for ( var i = 0; i < STATIC_GRAPHICS; i++ )
               gameImageBgrGraphics[i].draw();
          oPlayer.draw();
     }
         
     if ( GameState != STATE_LOAD_RESOURCES )
     { 
          gameImageLeftBtn.currentFrame  = optionLeftButton.prevState;
          gameImageRightBtn.currentFrame = optionRightButton.prevState;
          gameImageLeftBtn.draw();
          gameImageRightBtn.draw();
          if ( showMessage == true )
          {
               if ( canvasIsActivated == false )
               {
                    context.font = "16px Comic Sans MS";
                    context.fillStyle = "black";
                    context.fontWeight ="bold";
                    context.textAlign = "left";
                    context.fillText("Click anywhere on the stage to start", 100, gameImageLeftBtn.y[0] + 14);       
               }
               else if ( new Date().getTime() - timeOnCanvasActivation < 5000 ) // 5000ms
               {
                    context.font = "16px Comic Sans MS";
                    context.fillStyle = "black";
                    context.fontWeight ="bold";
                    context.textAlign = "left";
                    context.fillText("You can play also with the arrow keys", 100, gameImageLeftBtn.y[0] + 14);       
               }
               else
               {
                    showMessage = false; // so to not perform any kind of calculations/comparisons/etc...
               }
          }
     }
}



function calculate_positions()
{
     
}



function runGame() 
{
     if ( (previousInnerW != window.innerWidth) ||
          (previousInnerH != window.innerHeight) )
     {
          startGame(false);
          previousInnerW = window.innerWidth;
          previousInnerH = window.innerHeight;
     }

     clear();
     if ( GameState == STATE_LOAD_RESOURCES )
     {
          context.font = "24px Comic Sans MS";
          context.fillStyle = "red";
          context.textAlign = "center";
          context.fillText("Loading: " + ~~((iLoadedImages*100)/TOTAL_GRAPHICS_TO_LOAD) + " %" , 10, 150);       
     }
     else if ( GameState != STATE_LOAD_RESOURCES )
     {
          handleUserInput();
          if ( GameState == STATE_GAME_STARTED )
          {
               calculate_positions();
          }
     }
     onDrawFrame();
     setTimeout(runGame, 10);// every 10 milliseconds
};

No comments:

Post a Comment