HTML5 Game Development for Windows 8 #3: Finishing your first game

image

Welcome back to my HTML5 game programming series. In this part of the tutorial we are going to finish our first HTML5 game for Windows 8.

Note: This tutorial was written on the plane to Las Vegas, will fix typos at a later time.

If you havent completed my previous tutorials, now is the time 🙂

Anyhow. Last time we were left of with a playfield where you could move the hero around the screen, a main menu where you had to tap the screen to start the game, but no enemies. This is what we are going to implement today.

First of all, we need a few global variables for the ghost.

   1: var ghosts = [];

   2: var ghostSpeed = 1.0;

The first variable is an array including all the ghosts thats currently alive, and the 2nd one is the speed the ghosts will have when moving towards you.

We also need a global variable for the image and the bitmap of the ghost:
var ghostImage, ghostBitmap;

Next, we need a class for the ghost:

   1: function Ghost(gfx) {

   2:     this.positionX = Math.random() * 5000 - 2500;

   3:     this.positionY = Math.random() * 3000 - 1500;

   4:  

   5:     this.setStartPosition = function () {

   6:         if (this.positionX >= 0 && this.positionX <= window.innerWidth) {

   7:             this.positionX = -500;

   8:         }

   9:  

  10:         if (this.positionY >= 0 && this.positionY <= window.innerHeight) {

  11:             this.positionY = -500;

  12:         }

  13:     }

  14:  

  15:     this.targetX = 0;

  16:     this.targetY = 0;

  17:  

  18:     this.move = function (tX, tY) {

  19:         this.targetX = tX;

  20:         this.targetY = tY;

  21:  

  22:         if (this.targetX > this.positionX) {

  23:             this.positionX += ghostSpeed;

  24:         }

  25:         if (this.targetX < this.positionX) {

  26:             this.positionX -= ghostSpeed;

  27:         }

  28:         if (this.targetY > this.positionY) {

  29:             this.positionY += ghostSpeed;

  30:         }

  31:         if (this.targetY < this.positionY) {

  32:             this.positionY -= ghostSpeed;

  33:         }

  34:     };

  35:  

  36:     this.isCollision = function (playerX, playerY, playerW, playerH) {

  37:         var centerX = this.positionX + (this.ghostBitmap.image.width * scaleW / 2);

  38:         var centerY = this.positionY + (this.ghostBitmap.image.height * scaleH / 2);

  39:  

  40:         if ((centerX >= playerX - playerW / 2) && (centerX < playerX + playerW / 2)) {

  41:             if ((centerY >= playerY - playerH / 2) && (centerY < playerY + playerH / 2)) {

  42:                 return true;

  43:             }

  44:         }

  45:  

  46:         return false;

  47:     }

  48:  

  49:     this.ghostBitmap = gfx;

  50: }

  51:  

This class looks a bit like the player class – it got a variable for the current position of one ghost and the target. But this class also contains a function that moved the ghost towards the target, and a function that checks if the ghost is colliding with the player sprite.

The position of a new ghost will be randomly selected – but if it spawns in the middle of the playscreen, we move it outside so he wont spawn on top of the player making it impossible to avoid it. All ghosts must spawn outside the playfield, and move towards the player at all time.

Now, we load the image for the ghost like we did with the player in loadContent() – but we only load the image from the preload object:

   1: ghostImage = preload.getResult("ghost").result

 

Spawning a ghost

We will use a timer to spawn a ghost, so go ahead and create a new global variable that will contain the spawntime untill a new ghost is spawned:
var timeToAddNewGhost = 0;

In the update loop, just below the code that moves the player towards the target, we add the code that will add a new ghost to the ghosts array:

   1: timeToAddNewGhost -= 1;

   2: if (timeToAddNewGhost < 0) {

   3:     timeToAddNewGhost = 1000

   4:     ghosts.push(new Ghost(new createjs.Bitmap(ghostImage)));

   5:     ghosts[ghosts.length - 1].setStartPosition();

   6:     gameStage.addChild(ghosts[ghosts.length - 1].ghostBitmap);

   7: }

Every frame, we subtract one from the timeToAddNewGhost variable. If this is below 0, we set the timer to 1000 and add a new ghost to the array using the ghostImage we loaded earlier. We also make sure that the start position isn’t on the visible playfield.

For every 1000 frame, a new ghost is spawned and the game gets a bit harder. But an array wont do us much good without updating the position of the image file (or else the ghost will be invisible). So for each active ghost in the array, we set the image position, move the ghost towards the player and check for collision:

   1: for (var i = 0; i < ghosts.length; i++)

   2: {

   3:     ghosts[i].ghostBitmap.x = ghosts[i].positionX;

   4:     ghosts[i].ghostBitmap.y = ghosts[i].positionY;

   5:     ghosts[i].ghostBitmap.visible = true;

   6:     ghosts[i].move(player.positionX, player.positionY);

   7:     ghosts[i].isCollision(player.positionX, player.positionY, player.width, player.height);

   8: }

Just to make sure you got everything right, below is a listing of the entire source this far:

   1: // For an introduction to the Blank template, see the following documentation:

   2: // http://go.microsoft.com/fwlink/?LinkId=232509

   3: (function () {

   4:     "use strict";

   5:  

   6:     WinJS.Binding.optimizeBindingReferences = true;

   7:  

   8:     var app = WinJS.Application;

   9:     var activation = Windows.ApplicationModel.Activation;

  10:  

  11:     var canvas, context;

  12:     var gameStage;

  13:     var preload;

  14:  

  15:     var logoScreenImage, logoScreenBitmap;

  16:     var floorImage, floorBitmap;

  17:     var playerIdleImage, playerIdleBitmap;

  18:     var ghostImage, ghostBitmap;

  19:  

  20:     var newGame = true;

  21:  

  22:     var player;

  23:  

  24:     var ghosts = [];

  25:     var ghostSpeed = 1.0;

  26:     var timeToAddNewGhost = 0;

  27:  

  28:     var scaleW = window.innerWidth / 1366;

  29:     var scaleH = window.innerHeight / 768;

  30:  

  31:  

  32:     app.onactivated = function (args) {

  33:         if (args.detail.kind === activation.ActivationKind.launch) {

  34:             if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {

  35:                 // TODO: This application has been newly launched. Initialize

  36:                 // your application here.

  37:             } else {

  38:                 // TODO: This application has been reactivated from suspension.

  39:                 // Restore application state here.

  40:             }

  41:             args.setPromise(WinJS.UI.processAll());

  42:         }

  43:     };

  44:  

  45:     // The player class

  46:     function Player() {

  47:         this.positionX = window.innerWidth / 2;

  48:         this.positionY = window.innerHeight / 2;

  49:  

  50:         this.targetX = this.positionX;

  51:         this.targetY = this.positionY;

  52:  

  53:         this.width = playerIdleBitmap.image.width * scaleW;

  54:         this.height = playerIdleBitmap.image.height * scaleH;

  55:     }

  56:  

  57:     // The ghost class

  58:     function Ghost(gfx) {

  59:         this.positionX = Math.random() * 5000 - 2500;

  60:         this.positionY = Math.random() * 3000 - 1500;

  61:  

  62:         this.setStartPosition = function () {

  63:             if (this.positionX >= 0 && this.positionX <= window.innerWidth) {

  64:                 this.positionX = -500;

  65:             }

  66:  

  67:             if (this.positionY >= 0 && this.positionY <= window.innerHeight) {

  68:                 this.positionY = -500;

  69:             }

  70:         }

  71:  

  72:         this.targetX = 0;

  73:         this.targetY = 0;

  74:  

  75:         this.move = function (tX, tY) {

  76:             this.targetX = tX;

  77:             this.targetY = tY;

  78:  

  79:             if (this.targetX > this.positionX) {

  80:                 this.positionX += ghostSpeed;

  81:             }

  82:             if (this.targetX < this.positionX) {

  83:                 this.positionX -= ghostSpeed;

  84:             }

  85:             if (this.targetY > this.positionY) {

  86:                 this.positionY += ghostSpeed;

  87:             }

  88:             if (this.targetY < this.positionY) {

  89:                 this.positionY -= ghostSpeed;

  90:             }

  91:         };

  92:  

  93:         this.isCollision = function (playerX, playerY, playerW, playerH) {

  94:             var centerX = this.positionX + (this.ghostBitmap.image.width * scaleW / 2);

  95:             var centerY = this.positionY + (this.ghostBitmap.image.height * scaleH / 2);

  96:  

  97:             if ((centerX >= playerX - playerW / 2) && (centerX < playerX + playerW / 2)) {

  98:                 if ((centerY >= playerY - playerH / 2) && (centerY < playerY + playerH / 2)) {

  99:                     return true;

 100:                 }

 101:             }

 102:  

 103:             return false;

 104:         }

 105:  

 106:         this.ghostBitmap = gfx;

 107:     }

 108:  

 109:  

 110:     function pointerUp(event) {

 111:         if (newGame) {

 112:             newGame = false;

 113:         }

 114:         else {

 115:             player.targetX = event.x;

 116:             player.targetY = event.y;

 117:         }

 118:     }

 119:  

 120:     function pointerDown(event) {

 121:         if (newGame) {

 122:         }

 123:         else {

 124:             player.targetX = event.x;

 125:             player.targetY = event.y;

 126:         }

 127:     }

 128:  

 129:     function pointerMove(event) {

 130:         if (newGame) {

 131:         }

 132:         else {

 133:             player.targetX = event.x;

 134:             player.targetY = event.y;

 135:         }

 136:     }

 137:  

 138:     function initialize() {

 139:         canvas = document.getElementById("gameCanvas");

 140:         canvas.width = window.innerWidth;

 141:         canvas.height = window.innerHeight;

 142:         context = canvas.getContext("2d");

 143:  

 144:         canvas.addEventListener("MSPointerUp", pointerUp, false);

 145:         canvas.addEventListener("MSPointerMove", pointerMove, false);

 146:         canvas.addEventListener("MSPointerDown", pointerDown, false);

 147:  

 148:         gameStage = new createjs.Stage(canvas);

 149:  

 150:         loadContent();

 151:     }

 152:  

 153:     function loadContent() {

 154:         preload = new createjs.PreloadJS();

 155:         preload.onComplete = prepareStage;

 156:  

 157:         var manifest = [

 158:             { id: "logoScreen", src: "images/GFX/LogoScreen.png" },

 159:             { id: "floor", src: "images/GFX/floor.png" },

 160:             { id: "ghost", src: "images/GFX/Ghost.png" },

 161:             { id: "playerIdle", src: "images/GFX/PlayerIdle.png" }

 162:         ];

 163:  

 164:         preload.loadManifest(manifest);

 165:     }

 166:  

 167:     function prepareStage() {

 168:         logoScreenImage = preload.getResult("logoScreen").result;

 169:         logoScreenBitmap = new createjs.Bitmap(logoScreenImage);

 170:         logoScreenBitmap.scaleX = scaleW;

 171:         logoScreenBitmap.scaleY = scaleH;

 172:         gameStage.addChild(logoScreenBitmap);

 173:  

 174:         floorImage = preload.getResult("floor").result;

 175:         floorBitmap = new createjs.Bitmap(floorImage);

 176:         floorBitmap.visible = false;

 177:         floorBitmap.scaleX = scaleW;

 178:         floorBitmap.scaleY = scaleH;

 179:         gameStage.addChild(floorBitmap);

 180:  

 181:         playerIdleImage = preload.getResult("playerIdle").result;

 182:         playerIdleBitmap = new createjs.Bitmap(playerIdleImage);

 183:         playerIdleBitmap.visible = false;

 184:         playerIdleBitmap.scaleX = scaleW;

 185:         playerIdleBitmap.scaleY = scaleH;

 186:         gameStage.addChild(playerIdleBitmap);

 187:  

 188:         ghostImage = preload.getResult("ghost").result

 189:  

 190:         player = new Player();

 191:  

 192:         createjs.Ticker.setInterval(window.requestAnimationFrame);

 193:         createjs.Ticker.addListener(gameLoop);

 194:     }

 195:  

 196:     function gameLoop() {

 197:         update();

 198:         draw();

 199:     }

 200:  

 201:     function update() {

 202:         if (newGame) {

 203:             logoScreenBitmap.visible = true;

 204:             playerIdleBitmap.visible = false;

 205:             floorBitmap.visible = false;

 206:         }

 207:         else {

 208:             logoScreenBitmap.visible = false;

 209:             playerIdleBitmap.visible = true;

 210:             floorBitmap.visible = true;

 211:  

 212:             if (player.targetX > player.positionX) {

 213:                 player.positionX += 3;

 214:             }

 215:             if (player.targetX < player.positionX) {

 216:                 player.positionX -= 3;

 217:             }

 218:             if (player.targetY > player.positionY) {

 219:                 player.positionY += 3;

 220:             }

 221:             if (player.targetY < player.positionY) {

 222:                 player.positionY -= 3;

 223:             }

 224:  

 225:             timeToAddNewGhost -= 1;

 226:             if (timeToAddNewGhost < 0) {

 227:                 timeToAddNewGhost = 1000

 228:                 ghosts.push(new Ghost(new createjs.Bitmap(ghostImage)));

 229:                 ghosts[ghosts.length - 1].setStartPosition();

 230:                 gameStage.addChild(ghosts[ghosts.length - 1].ghostBitmap);

 231:             }

 232:  

 233:             for (var i = 0; i < ghosts.length; i++) {

 234:                 ghosts[i].ghostBitmap.x = ghosts[i].positionX;

 235:                 ghosts[i].ghostBitmap.y = ghosts[i].positionY;

 236:                 ghosts[i].ghostBitmap.visible = true;

 237:                 ghosts[i].move(player.positionX, player.positionY);

 238:                 ghosts[i].isCollision(player.positionX, player.positionY, player.width, player.height);

 239:             }

 240:  

 241:             playerIdleBitmap.x = player.positionX - (player.width / 2);

 242:             playerIdleBitmap.y = player.positionY - (player.height / 2);

 243:         }

 244:     }

 245:  

 246:     function draw() {

 247:         gameStage.update();

 248:     }

 249:  

 250:     app.oncheckpoint = function (args) {

 251:         // TODO: This application is about to be suspended. Save any state

 252:         // that needs to persist across suspensions here. You might use the

 253:         // WinJS.Application.sessionState object, which is automatically

 254:         // saved and restored across suspension. If you need to complete an

 255:         // asynchronous operation before your application is suspended, call

 256:         // args.setPromise().

 257:     };

 258:  

 259:     document.addEventListener("DOMContentLoaded", initialize, false);

 260:  

 261:     app.start();

 262: })();

 263:  

 264:  

 265:  

If you run the game now, the game will start, you can move around and more and more ghosts will try to attack you.

Implementing Game Over
The ghosts aren’t really dangerous right now, if the ghost hit you – nothing happens. Yes, we are checking for collision, but haven’t yet created any rules that will happen when the collision is true.

To do this we need to define a new global variable:

   1: var isGameOver = false;

In the loop where we go through each of the ghosts, replace this line:
ghosts[i].isCollision(player.positionX, player.positionY, player.width, player.height);

with this:

   1: isGameOver = ghosts[i].isCollision(player.positionX, player.positionY, player.width, player.height);

Also, add this code below so when a collision is happening, we break the for-loop:

   1: if (isGameOver)   

   2:     break;

In the top of the update() function, inside the else statement of if(newGame), add the following code:

   1: if (isGameOver) {

   2:     isGameOver = false;

   3:     ghosts.length = 0;

   4:     gameStage.clear();

   5:  

   6:     gameStage.addChild(logoScreenBitmap);

   7:     gameStage.addChild(floorBitmap);

   8:     gameStage.addChild(playerIdleBitmap);

   9:     gameStage.addChild(scoreText);

  10:  

  11:     gameStage.update();

  12: }

  13:  

Just to make sure you are in thr right place, the update function looks like this:

   1: function update() {

   2:     if (newGame) {

   3:         logoScreenBitmap.visible = true;

   4:         playerIdleBitmap.visible = false;

   5:         floorBitmap.visible = false;

   6:     }

   7:     else {

   8:         if (isGameOver) {

   9:             isGameOver = false;

  10:             ghosts.length = 0;

  11:             gameStage.clear();

  12:  

  13:             gameStage.addChild(logoScreenBitmap);

  14:             gameStage.addChild(floorBitmap);

  15:             gameStage.addChild(playerIdleBitmap);

  16:  

  17:             gameStage.update();

  18:         }

  19:         logoScreenBitmap.visible = false;

  20:         playerIdleBitmap.visible = true;

  21:         floorBitmap.visible = true;

  22:  

  23:         if (player.targetX > player.positionX) {

  24:             player.positionX += 3;

  25:         }

  26:         if (player.targetX < player.positionX) {

  27:             player.positionX -= 3;

  28:         }

  29:         if (player.targetY > player.positionY) {

  30:             player.positionY += 3;

  31:         }

  32:         if (player.targetY < player.positionY) {

  33:             player.positionY -= 3;

  34:         }

  35:  

  36:         timeToAddNewGhost -= 1;

  37:         if (timeToAddNewGhost < 0) {

  38:             timeToAddNewGhost = 1000

  39:             ghosts.push(new Ghost(new createjs.Bitmap(ghostImage)));

  40:             ghosts[ghosts.length - 1].setStartPosition();

  41:             gameStage.addChild(ghosts[ghosts.length - 1].ghostBitmap);

  42:         }

  43:  

  44:         for (var i = 0; i < ghosts.length; i++) {

  45:             ghosts[i].ghostBitmap.x = ghosts[i].positionX;

  46:             ghosts[i].ghostBitmap.y = ghosts[i].positionY;

  47:             ghosts[i].ghostBitmap.visible = true;

  48:             ghosts[i].move(player.positionX, player.positionY);

  49:             isGameOver = ghosts[i].isCollision(player.positionX, player.positionY, player.width, player.height);

  50:             if (isGameOver)

  51:                 break;

  52:         }

  53:  

  54:         playerIdleBitmap.x = player.positionX - (player.width / 2);

  55:         playerIdleBitmap.y = player.positionY - (player.height / 2);

  56:     }

  57: }

  58:  

What we do in this code is to check if it’s game over. If this is true (a collision is happened), we set the gameOver flag to false again, clear all the active ghosts in the array and remove everything from the gameState (because its filled with ghosts). We then add back the logoScreen, the floor and the player and restart the game.

If you run the game again, you can see that we have a working game! 🙂

Adding a score

But, something is missing – a score!
The longer you stay alive, the more score you will get.

To add a score, define a new global variable called playerScore and a a new global variable called scoreText:

   1: var scoreText;

   2: var playerScore = 0;

scoreText will contain the font and position of the text.

In the game over “routine”, add a line that sets the playerScore to 0 – when it’s game over, we want to reset the score:

   1: playerScore = 0;

Back in loadContent(): We create a new font object using CreateJS containing the text we want to display, the position (we want it to be on the top center of the screen) and add it to the gameStage:

   1: scoreText = new createjs.Text("Score: " + playerScore, "30px sans-serif", "yellow");

   2: scoreText.x = canvas.width / 2 - (scoreText.getMeasuredWidth() * scaleW / 2);

   3: scoreText.scaleX = scaleW;

   4: scoreText.scaleY = scaleH;

   5: scoreText.y = 30 * scaleH;

   6: scoreText.visible = false;

   7: gameStage.addChild(scoreText);

The function getMeasuredWidth() simply returns how wide the string that we want to render is – making it simple to calculate where to render it based on how long the text is.

Back again to the gameOver rules: Add a new line that adds the scoreText object back to the stage:

   1: gameStage.addChild(scoreText);

Still in the update() function, just below the code that moves the player – add these two lines of code:

   1: playerScore += 1;

   2: scoreText.text = ("Score: " + playerScore);

This increases the score for each frame and updates the text in scoreText.

We also must make sure that the score text is visible or invisible based on if we are in game or not.

image

And thats it, we got a working game! If something isn’t working, you can check your solution with the code below, it contains all the code for the game:

   1: // For an introduction to the Blank template, see the following documentation:

   2: // http://go.microsoft.com/fwlink/?LinkId=232509

   3: (function () {

   4:     "use strict";

   5:  

   6:     WinJS.Binding.optimizeBindingReferences = true;

   7:  

   8:     var app = WinJS.Application;

   9:     var activation = Windows.ApplicationModel.Activation;

  10:  

  11:     var canvas, context;

  12:     var gameStage;

  13:     var preload;

  14:  

  15:     var logoScreenImage, logoScreenBitmap;

  16:     var floorImage, floorBitmap;

  17:     var playerIdleImage, playerIdleBitmap;

  18:     var ghostImage, ghostBitmap;

  19:  

  20:     var newGame = true;

  21:     var isGameOver = false;

  22:  

  23:     var player;

  24:  

  25:     var scoreText;

  26:     var playerScore = 0;

  27:  

  28:     var ghosts = [];

  29:     var ghostSpeed = 1.0;

  30:     var timeToAddNewGhost = 0;

  31:  

  32:     var scaleW = window.innerWidth / 1366;

  33:     var scaleH = window.innerHeight / 768;

  34:  

  35:  

  36:     app.onactivated = function (args) {

  37:         if (args.detail.kind === activation.ActivationKind.launch) {

  38:             if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {

  39:                 // TODO: This application has been newly launched. Initialize

  40:                 // your application here.

  41:             } else {

  42:                 // TODO: This application has been reactivated from suspension.

  43:                 // Restore application state here.

  44:             }

  45:             args.setPromise(WinJS.UI.processAll());

  46:         }

  47:     };

  48:  

  49:     // The player class

  50:     function Player() {

  51:         this.positionX = window.innerWidth / 2;

  52:         this.positionY = window.innerHeight / 2;

  53:  

  54:         this.targetX = this.positionX;

  55:         this.targetY = this.positionY;

  56:  

  57:         this.width = playerIdleBitmap.image.width * scaleW;

  58:         this.height = playerIdleBitmap.image.height * scaleH;

  59:     }

  60:  

  61:     // The ghost class

  62:     function Ghost(gfx) {

  63:         this.positionX = Math.random() * 5000 - 2500;

  64:         this.positionY = Math.random() * 3000 - 1500;

  65:  

  66:         this.setStartPosition = function () {

  67:             if (this.positionX >= 0 && this.positionX <= window.innerWidth) {

  68:                 this.positionX = -500;

  69:             }

  70:  

  71:             if (this.positionY >= 0 && this.positionY <= window.innerHeight) {

  72:                 this.positionY = -500;

  73:             }

  74:         }

  75:  

  76:         this.targetX = 0;

  77:         this.targetY = 0;

  78:  

  79:         this.move = function (tX, tY) {

  80:             this.targetX = tX;

  81:             this.targetY = tY;

  82:  

  83:             if (this.targetX > this.positionX) {

  84:                 this.positionX += ghostSpeed;

  85:             }

  86:             if (this.targetX < this.positionX) {

  87:                 this.positionX -= ghostSpeed;

  88:             }

  89:             if (this.targetY > this.positionY) {

  90:                 this.positionY += ghostSpeed;

  91:             }

  92:             if (this.targetY < this.positionY) {

  93:                 this.positionY -= ghostSpeed;

  94:             }

  95:         };

  96:  

  97:         this.isCollision = function (playerX, playerY, playerW, playerH) {

  98:             var centerX = this.positionX + (this.ghostBitmap.image.width * scaleW / 2);

  99:             var centerY = this.positionY + (this.ghostBitmap.image.height * scaleH / 2);

 100:  

 101:             if ((centerX >= playerX - playerW / 2) && (centerX < playerX + playerW / 2)) {

 102:                 if ((centerY >= playerY - playerH / 2) && (centerY < playerY + playerH / 2)) {

 103:                     return true;

 104:                 }

 105:             }

 106:  

 107:             return false;

 108:         }

 109:  

 110:         this.ghostBitmap = gfx;

 111:     }

 112:  

 113:  

 114:     function pointerUp(event) {

 115:         if (newGame) {

 116:             newGame = false;

 117:         }

 118:         else {

 119:             player.targetX = event.x;

 120:             player.targetY = event.y;

 121:         }

 122:     }

 123:  

 124:     function pointerDown(event) {

 125:         if (newGame) {

 126:         }

 127:         else {

 128:             player.targetX = event.x;

 129:             player.targetY = event.y;

 130:         }

 131:     }

 132:  

 133:     function pointerMove(event) {

 134:         if (newGame) {

 135:         }

 136:         else {

 137:             player.targetX = event.x;

 138:             player.targetY = event.y;

 139:         }

 140:     }

 141:  

 142:     function initialize() {

 143:         canvas = document.getElementById("gameCanvas");

 144:         canvas.width = window.innerWidth;

 145:         canvas.height = window.innerHeight;

 146:         context = canvas.getContext("2d");

 147:  

 148:         canvas.addEventListener("MSPointerUp", pointerUp, false);

 149:         canvas.addEventListener("MSPointerMove", pointerMove, false);

 150:         canvas.addEventListener("MSPointerDown", pointerDown, false);

 151:  

 152:         gameStage = new createjs.Stage(canvas);

 153:  

 154:         loadContent();

 155:     }

 156:  

 157:     function loadContent() {

 158:         preload = new createjs.PreloadJS();

 159:         preload.onComplete = prepareStage;

 160:  

 161:         var manifest = [

 162:             { id: "logoScreen", src: "images/GFX/LogoScreen.png" },

 163:             { id: "floor", src: "images/GFX/floor.png" },

 164:             { id: "ghost", src: "images/GFX/Ghost.png" },

 165:             { id: "playerIdle", src: "images/GFX/PlayerIdle.png" }

 166:         ];

 167:  

 168:         preload.loadManifest(manifest);

 169:     }

 170:  

 171:     function prepareStage() {

 172:         logoScreenImage = preload.getResult("logoScreen").result;

 173:         logoScreenBitmap = new createjs.Bitmap(logoScreenImage);

 174:         logoScreenBitmap.scaleX = scaleW;

 175:         logoScreenBitmap.scaleY = scaleH;

 176:         gameStage.addChild(logoScreenBitmap);

 177:  

 178:         floorImage = preload.getResult("floor").result;

 179:         floorBitmap = new createjs.Bitmap(floorImage);

 180:         floorBitmap.visible = false;

 181:         floorBitmap.scaleX = scaleW;

 182:         floorBitmap.scaleY = scaleH;

 183:         gameStage.addChild(floorBitmap);

 184:  

 185:         playerIdleImage = preload.getResult("playerIdle").result;

 186:         playerIdleBitmap = new createjs.Bitmap(playerIdleImage);

 187:         playerIdleBitmap.visible = false;

 188:         playerIdleBitmap.scaleX = scaleW;

 189:         playerIdleBitmap.scaleY = scaleH;

 190:         gameStage.addChild(playerIdleBitmap);

 191:  

 192:         scoreText = new createjs.Text("Score: " + playerScore, "30px sans-serif", "yellow");

 193:         scoreText.x = canvas.width / 2 - (scoreText.getMeasuredWidth() * scaleW / 2);

 194:         scoreText.scaleX = scaleW;

 195:         scoreText.scaleY = scaleH;

 196:         scoreText.y = 30 * scaleH;

 197:         scoreText.visible = false;

 198:         gameStage.addChild(scoreText);

 199:  

 200:  

 201:         ghostImage = preload.getResult("ghost").result

 202:  

 203:         player = new Player();

 204:  

 205:         createjs.Ticker.setInterval(window.requestAnimationFrame);

 206:         createjs.Ticker.addListener(gameLoop);

 207:     }

 208:  

 209:     function gameLoop() {

 210:         update();

 211:         draw();

 212:     }

 213:  

 214:     function update() {

 215:         if (newGame) {

 216:             logoScreenBitmap.visible = true;

 217:             playerIdleBitmap.visible = false;

 218:             floorBitmap.visible = false;

 219:             scoreText.visible = false;

 220:         }

 221:         else {

 222:             if (isGameOver) {

 223:                 isGameOver = false;

 224:                 ghosts.length = 0;

 225:                 playerScore = 0;

 226:                 gameStage.clear();

 227:  

 228:                 gameStage.addChild(logoScreenBitmap);

 229:                 gameStage.addChild(floorBitmap);

 230:                 gameStage.addChild(playerIdleBitmap);

 231:                 gameStage.addChild(scoreText);

 232:  

 233:                 gameStage.update();

 234:             }

 235:             logoScreenBitmap.visible = false;

 236:             playerIdleBitmap.visible = true;

 237:             floorBitmap.visible = true;

 238:             scoreText.visible = true;

 239:  

 240:             if (player.targetX > player.positionX) {

 241:                 player.positionX += 3;

 242:             }

 243:             if (player.targetX < player.positionX) {

 244:                 player.positionX -= 3;

 245:             }

 246:             if (player.targetY > player.positionY) {

 247:                 player.positionY += 3;

 248:             }

 249:             if (player.targetY < player.positionY) {

 250:                 player.positionY -= 3;

 251:             }

 252:  

 253:             playerScore += 1;

 254:             scoreText.text = ("Score: " + playerScore);

 255:  

 256:             timeToAddNewGhost -= 1;

 257:             if (timeToAddNewGhost < 0) {

 258:                 timeToAddNewGhost = 1000

 259:                 ghosts.push(new Ghost(new createjs.Bitmap(ghostImage)));

 260:                 ghosts[ghosts.length - 1].setStartPosition();

 261:                 gameStage.addChild(ghosts[ghosts.length - 1].ghostBitmap);

 262:             }

 263:  

 264:             for (var i = 0; i < ghosts.length; i++) {

 265:                 ghosts[i].ghostBitmap.x = ghosts[i].positionX;

 266:                 ghosts[i].ghostBitmap.y = ghosts[i].positionY;

 267:                 ghosts[i].ghostBitmap.visible = true;

 268:                 ghosts[i].move(player.positionX, player.positionY);

 269:                 isGameOver = ghosts[i].isCollision(player.positionX, player.positionY, player.width, player.height);

 270:                 if (isGameOver)

 271:                     break;

 272:             }

 273:  

 274:             playerIdleBitmap.x = player.positionX - (player.width / 2);

 275:             playerIdleBitmap.y = player.positionY - (player.height / 2);

 276:         }

 277:     }

 278:  

 279:     function draw() {

 280:         gameStage.update();

 281:     }

 282:  

 283:     app.oncheckpoint = function (args) {

 284:         // TODO: This application is about to be suspended. Save any state

 285:         // that needs to persist across suspensions here. You might use the

 286:         // WinJS.Application.sessionState object, which is automatically

 287:         // saved and restored across suspension. If you need to complete an

 288:         // asynchronous operation before your application is suspended, call

 289:         // args.setPromise().

 290:     };

 291:  

 292:     document.addEventListener("DOMContentLoaded", initialize, false);

 293:  

 294:     app.start();

 295: })();

 296:  

Thanks for following! In the next tutorial, we are going to look at other important aspects of game programming on Windows 8 like storeing data and so on.

Download the source of the game here:
http://sdrv.ms/T2KTFg

This entry was posted in Game programming, Tutorial, Windows 8. Bookmark the permalink.

10 Responses to HTML5 Game Development for Windows 8 #3: Finishing your first game

  1. codex 5 says:

    Hi, thanks for this great tutorial! can I reuse your code for a similar game?

  2. Pingback: HTML5 Game Starter Kit for Windows 8 | digitalerr0r

  3. gamer12345 says:

    thanks man great tutorial

  4. Pingback: Windows 8 Games with CreateJS Tutorials & Resources « skater coder

  5. Clive says:

    Great tutorial. Thanks a lot. 🙂
    I plan to navigate to different page instead of hide/show an image.
    However i encounter an error when navigate. It said:
    Unhandled exception at line 81, column 326 in ms-appx://4c985e5d-0546-41d7-9f24-e945d98a9232/js/CreateJS/easeljs-0.5.0.min.js
    0x80004005 – JavaScript runtime error: Unspecified error.
    What should i put as the second parameter(initial state) of WinJS.Navigation.navigate()?

  6. Pingback: Game Starterkit for Windows 8 with leaderboard in Windows Azure | digitalerr0r

  7. Pingback: You can lead a dev to... "Game Starter Kit for Windows 8 with leaderboard in Windows Azure" - Windows Azure Blog

  8. abhinav says:

    am getting the following error

    157: preload = new createjs.PreloadJS();
    158: preload.onComplete = prepareStage;
    “Unhandled exception at line 157, column 18 in ms-appx://e873b1a2-9c7e-4668-8426-b5cba485e4ef/js/default.js

    0x800a01bd – JavaScript runtime error: Object doesn’t support this action

    If there is a handler for this exception, the program may be safely continued.”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.