Game Starterkit for Windows 8 with leaderboard in Windows Azure

image

This Game Starter Kit for Windows 8 contains what you need to get started with game programming for Windows 8, including online high score in Windows Azure. The game kit is written in HTML and JavaScript.

image
Download now – follow instructions below to make it work.

Before we start, let me show you what you get with this solution.

Example game
The kit comes with an example game that you can modify and play with. The code is all in one file so it’s easy to play with for beginners. Feel free to use it however you like.

The kit will get updated with cooler and more advanced game that you can use as a basis when developing your own. Follow this blog to get the updates!

image

If you want to learn how to create this game that comes with this starter kit, you can read the guide here (slightly different graphics):

Part I:
http://digitalerr0r.wordpress.com/2012/09/19/html5-game-development-for-windows-8-1-getting-started/

Part II:
http://digitalerr0r.wordpress.com/2012/09/20/html5-game-development-for-windows-8-2-adding-a-player/

Part III:
http://digitalerr0r.wordpress.com/2012/09/20/html5-game-development-for-windows-8-3-finishing-your-first-game/

Implementation of high score in Windows Azure
The kit also enables support for Windows Azure and handles the connection, and got functions to check if you are having a internet connection. You need to have your own Windows Azure account, and set up the service/datatable yourself.

 

imageSnap view

The snap view is required for a store app. Your app needs to have this implemented in one way or another. This kit simply renders another div tag with an image saying it isn’t supported. Be creative with this – show your leaderboard here?

Remember to change this image so it suits your own app.

Insert your logo or something.

Tiles

The starter kit comes with basic tiles, change these to your own when you create a new game. Also a splash screen is included.

logosmalllogosplashscreenstorelogowidelogo

 

Settings charm implementation
image

Every app needs a Privacy Policy, an About page and it’s nice to have a settings page where you can set spesific settings for your game like enable online highscores and so on.

The starter kit comes with an about page, change this so it fits your game.

It also comes with a basic pricay policy, change this so it fits your game.

It also comes with a settings page with a toggle that enables you to disable or enable the online highscore system (some players would prefer to just play offline).

imageimageimage

 

Let’s get you up and running!

Follow this guide to get everything up and running with the starter kit.

1) Install the tools

To get started, you need Visual Studio 2012 for Windows 8, so go ahead and download the package now if you don’t have this already.

2) Get an account on Windows Azure

Next, you will need to get an account for Windows Azure. There is a free trial where you can try it out, or if you are a BizSpark member (free membership), or an MSDN subscriper, you get a lot for free so check out your possibilities. But the cost of this solution ( a simple mobile service with 1gb SQL database) is very cheap.

The following is a guide that you need to follow in order to set up your Windows Azure solution correctly, so it works with the starter kit. When you get this up and running, and get to build/play the example game in the Starter Kit, you are ready to start modifying the code and create your own games!

This will usually only take about 10 minutes, so follow along and I’ll make sure to get you up and running in no time!

Once you are registered for Windows Azure, go to http://manage.windowsazure.com and log in with your shiny new fresh account.

3) Create your Mobile Service and Database

What we want is to have a mobile service that’s available for our app to use. It will consist of one Mobile Service, and one table in a database – all up in the modern cloud arcitecture.

Click the New button.

image_thumb6

new menu will pop up. Select Compute –> Mobile Service –> Create to start a wizard where you can implement your mobile service.

image_thumb9

A wizard will pop up and take you through the steps. Set the name of the URL to htmlstarerkit or anything you like, choose to create a new database on the subscription you just set up, and locate it in a region near you. Click the next arrow.

Next, you need to configure the database.
image_thumb16

Once you have filled out your details (fill inn whats good for you, it doesnt matter for this solution – you need something you can use yourself), click the OK tick!
Your new database and mobile service will be set up within a few minutes.

4) Configure

Once it’s ready, click on the Mobile Services tab, and click the arrow on your new service.
image_thumb19

It will take you to the mobile services menu for the selected service.

image_thumb22

We don’t need to create a new app as we got an existing one (you will download the solution soon). So, go ahead and click on the “Connect an existing Windows Store app”.

A list of steps that you can follow to implement everything will be shown:
image_thumb27

The first is installing the SDK. Click the link and install what you need.

Next, we need to download the solution. You can download the project from here:

image
Download now

Now, open the solution you just downloaded and make sure the references are OK and right according to Step 2 in the Mobile Services step. It should have been added in the solution, but there might be some differences in your paths and mine so check that it’s correct.

image

The next thing we need to do is to set the correct Application Key in our app.

If you take another look at Step 2 in the Mobile Services step, you see your URL and the Application key of your service. Take a note of these two, we will insert them to your game now.

Go to the solution in Visual Studio and open game.js:

image

Locate the client variable near the top of your solution and insert the URL you created for the Mobile Service, and the Application Key that got generated (as noted in the previous step).

image

Now, our client is ready, but the Mobile Service is still missing one thing: the table that will contain our data!

Let’s create that (this is the last thing we need to do):

Locate the Data link on the top of the page you are at now.
image_thumb37

Click it.

You will now be taken in to the Data page of the service. Click Create.

image

A popup will show.

Create a new table named starterkittable:

image

Now we have a new table called starterkittable, and by default your table should have a dynamic schema. This means that if you try to insert anything into the table, and that insert contains columns that doesnt exist in our table, the service will create it for you.

So right now, your new table consists only of an Id column.

5) Build your solution and make sure it works

Go back to the solution, make sure you have the correct URL and Application Key in your solution, and then Build and run the project.

The game starts up, and when you die, your score will be uploaded to this table. Check that the column Score is created, and that it contains the correct score value you wanted.

 

image

Go to the Data tab and the table to see your scores:

image

Now, be creative. There are a lot of funny little games you can create by just modifying this simple template.

Enjoy!

image
Download now

Posted in Tutorial | 11 Comments

Using Windows Azure Mobile Services in your apps

image

Windows Azure Mobile Services makes it really easy (almost too easy) to implement services like online leaderboards, services/compute, databases, server side logic and so on that can be used in your apps!

I would recommend everyone to take a look at Windows Azure and the Mobile Services. There is a free trial where you can try it out, or if you are a BizSpark member (free membership), or an MSDN subscriper, you get a lot for free so check out your possibilities. But the cost of this solution ( a simple mobile service with 1gb SQL database) is very cheap.

You can follow this guide to implement Windows Azure Mobile Services in any of your own solutions.

First of all, I needed a Windows Azure subscription. Since I had one from before, it was just about signing in at manage.windowsazure.com/.

Next, I needed a mobile service that my app could use. When logged in to the portal I was presented with my Azure Account, an overview and the possibility to add a new service.

Click the New button.
image_thumb6

A new menu will pop up. Select Compute –> Mobile Service –> Create to start a wizard where you can implement your mobile service.
image_thumb9

The wizard will take you through everything you need to set it up:

image_thumb14

In the first screen, set the URL to something spesific for your project, like the name of the game. You can create a new database (if you dont have one from before), select your supscription (if you have multiple like I do) and what region you want your service to be in.

image_thumb16

Hit the tick to create your new service and database. It will take a few seconds for Azure to set everything up for you but THAT’S IT for creating a new service and a database (no need to install and set this up for yourself and spend a day on doing that).

Now, when the service is ready, you can see it in the overview, or in the Mobile Services tab.

image_thumb19

Click on the arrow next to the name of the newly created service to start confiuring it and implement it.

You will be presented with a screen like this:
image_thumb22

Now, I already had my app created so I clicked on the “Connect an existing Windows Store app”.

A short list of steps that you can follow to implement everything will be shown:
image_thumb27

The first is installing the SDK. Click the link and install what you need.

image_thumb26

The next thing you need is to Add the Windows Azure Mobile Services Javascript Client extention to your project in Visual Studio 2012. I chose JavaScript here because my solution is JavaScript and HTML5 based.

To do this, just add a reference like you normally do in Visual Studio.

image_thumb31

Then I added the client object that is used to invoke our Mobile Service currently running in the cloud.

var client = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient( “https://yoursite.azure-mobile.net/”, “your own key” );

That’s what you need in your app in order to invoke whatever you have in your Mobile Service. Smilefjes

Now, we need a table in our SQL database that will contain our data.

image_thumb34

Click the “Create Item Table” button and a new table is created.

Now, let’s change the table so it fits our needs. Go to the Databases tab from the main overview of the Azure Portal.
image_thumb43

Press the arrow of your databse to go to the database tools. From there, press Manage.
image_thumb46

Now you have to log in to your database. Enter the creadentials you proveded when you created the database and log in.

When in, click on the Design tab and select the Item table you created above.
image_thumb48

Change the table so it looks like this, but make sure it fits your needs.
image_thumb51

Press Save! We are now ready to invoke our mobile service. Let’s just take a quick look at it just so you know where you can find it.

From the Mobile Services project page in Azure, find the Data tab and click it.
image_thumb37

Open the Item table by clicking the arrow:
image_thumb40

Click on the Scripts tab to see all the scripts that are available for this table.
image_thumb54

You can also add functionality here. Select Insert or Read or Update or Delete and change the code based on your needs (You might want Read() to only return X number of results, sorted in one way ++).

Ok, let’s add data to this table. Open your solution in Visual Studio 2012 and add the two fucntions below (modify to make it fit your table in Azure):

The getScoreBoardfromAzure funtion gets all results that fits the current GameMode (could be a spesific Level also), ordered by Score, and take the top 10. Once this function is done, it adds the results to a list.

   1: getScoreBoardFromAzure: function () {

   2:     var _that = this;

   3:     this.scoreTable.where({ GameMode: this.gameMode })

   4:         .orderByDescending("Score").take(10)

   5:         .read()

   6:         .done(function (results) {

   7:             _that.scoreItems = new WinJS.Binding.List(results);

   8:         });

   9: },

The insertScoreToAzure function adds an item to the table in Windows Azure. Once a new entry is added, update the table in your local client.

   1: insertScoreToAzure: function (scoreItem) {

   2:     var _that = this;

   3:     this.scoreTable.insert(scoreItem).done(function () {

   4:         _that.getScoreBoardFromAzure();

   5:     });

   6: },

Go to the code where you want a new entry to Windows Azure to be added and call the function above like this (change so it fits your table):

   1: this.insertScoreToAzure({

   2:     Name: this.playerName,

   3:     Score: this.score,

   4:     GameMode: this.gameMode

   5: });

Go back to Windows Azure and see your new data in action!

Make sure to crate a screen that renders the content of the list that contains the data the server returns to you when needed, or have a seperate page show it, or the snap view mode of the game.

So, this is how I imlemented the first version of my Online Leaderboards hosted in Windows Azure for Lumilight.

Posted in Uncategorized | 2 Comments

The Making of Lumilight, a game for Windows 8

lumilight

Lumilight is a very simple game created for Windows 8, with Online Leaderboards hosted in Windows Azure. Your main goal is to gather as much light as possible from fireflies within 60 seconds.

The game is all about reacting quickly, and hitting the target as soon as possible.

You can download the game here: Download now

Note: The latest release will be out in a few days (Written on: April 3rd 2013)

1 – How the game works

The game comes with 3 different game modes:

screenshot_03222013_041203

The single fly mode will only have one fly visible at the same time. It’s all about having one target and following that. A fly will spawn when another looses all light.

The multi fly mode got multiple flies you can take out, and more will spawn as the time goes. A new fly will also spawn when another looses all light.

The Chaos mode is what it says, chaos. It spawns a lot of flies. How many can you take out? Get help from friends and tap those flies!

To get light, you just have to tap them. Each firefly needs to be tapped 5 times before you gather the light.

firefly_0

This image show the different stages, from full light on the left, to almost depleted on the right.

When the firefly it all out of light it will disapare, and you will get +1 light.
pluss

When you hit a firefly, a bonus combo will start. The more fireflies you hit without missing, the higher the bonus multiplier will get.

comboPieSet

If you miss, the combo multiplier will reset, and the amount of points you get will go back to normal.

Each tap will give you some points, and for each light you gather you will get a lot of points. You will also loose a small amount of points if you miss (also resetting the multiplier).

Screenshot (3)

The interface gives you some information like who the player is (can be set from the settings charm in Windows 8), the time left, your current combo, how many lights you have gathered and your total score.

When the round is over, you get to a screen that shows you some stats like how many lights you got, the total score, your longest combo streak this round and the accuracy. Also, you will see both the local highscore and the online leaderboard. Can you beat the best?

Screenshot (4)
Local leaderboard

Screenshot (5)
Online leaderboard

2 – Game logic

The game logic is simple. When you tap a fly, it will give you points. When you gather a light, you will get more points. And if you have a bonus multiplier, that score will be multiplied.

For each fly you take out, another will spawn. The faster and more accurate you are, the more points will you get.

You got 60 seconds.

A.I: The life of a firefly
The fly will randomly move based on a simple A.I (Artificial Intelligence) routine. When it spawns, a random coordinate on the screen will be given as the “spawnpoint”, and a random starting direction will be given. Then the fly flies around on the screen in “jumpy” movements, and might decide to change the direction along the X-axis, or how much it will move up or down.

If the fly is hit, a sound effect is played and the fly might decide to change the direction, or follow the same path it had.

image

3 – Technical details

The game is implemented using HTML5 and JavaScript with addition of the ImpactJS Library modified for Windows 8. The online leaderboard is implemented in Windows Aure and JavaScript.

Sound effects are simply recorded in a silent room using human voices.

4 – Online highscores in Windows Azure

The most awesome part of this game is the implementation of Windows Azure and how simple it was. It took me about 50 minutes to get my online leaderboard for the game set up and working from in game.

I would recommend everyone to take a look at Windows Azure and the Mobile Services. There is a free trial where you can try it out, or if you are a BizSpark member (free membership), or an MSDN subscriper, you get a lot for free so check out your possibilities. But the cost of this solution ( a simple mobile service with 1gb SQL database) is very cheap.

I decided to add how I implemented Windows Azure in this solution as it’s so simple. So here comes a guide on how I added support for Windows Azure in this solution!

You can follow this guide to implement Windows Azure Mobile Services in any of your own solutions.

First of all, I needed a Windows Azure subscription. Since I had one from before, it was just about signing in at manage.windowsazure.com/.

Next, I needed a mobile service that my app could use. When logged in to the portal I was presented with my Azure Account, an overview and the possibility to add a new service.

Click the New button.
image

A new menu will pop up. Select Compute –> Mobile Service –> Create to start a wizard where you can implement your mobile service.
image

The wizard will take you through everything you need to set it up:

image

In the first screen, set the URL to something spesific for your project, like the name of the game. You can create a new database (if you dont have one from before), select your supscription (if you have multiple like I do) and what region you want your service to be in.

image

Hit the tick to create your new service and database. It will take a few seconds for Azure to set everything up for you but THAT’S IT for creating a new service and a database (no need to install and set this up for yourself and spend a day on doing that).

Now, when the service is ready, you can see it in the overview, or in the Mobile Services tab.

image

Click on the arrow next to the name of the newly created service to start confiuring it and implement it.

You will be presented with a screen like this:
image

Now, I already had my app created so I clicked on the “Connect an existing Windows Store app”.

A short list of steps that you can follow to implement everything will be shown:
image

The first is installing the SDK. Click the link and install what you need.

image

The next thing you need is to Add the Windows Azure Mobile Services Javascript Client extention to your project in Visual Studio 2012. I chose JavaScript here because my solution is JavaScript and HTML5 based.

To do this, just add a reference like you normally do in Visual Studio.

image

Then I added the client object that is used to invoke our Mobile Service currently running in the cloud.

var client = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient( “https://yoursite.azure-mobile.net/”, “your own key” );

That’s what you need in your app in order to invoke whatever you have in your Mobile Service. Smilefjes

Now, we need a table in our SQL database that will contain our data.

image

Click the “Create Item Table” button and a new table is created.

Now, let’s change the table so it fits our needs. Go to the Databases tab from the main overview of the Azure Portal.
image

Press the arrow of your databse to go to the database tools. From there, press Manage.
image

Now you have to log in to your database. Enter the creadentials you proveded when you created the database and log in.

When in, click on the Design tab and select the Item table you created above.
image

Change the table so it looks like this, but make sure it fits your needs.
image

Press Save! We are now ready to invoke our mobile service. Let’s just take a quick look at it just so you know where you can find it.

From the Mobile Services project page in Azure, find the Data tab and click it.
image

Open the Item table by clicking the arrow:
image

Click on the Scripts tab to see all the scripts that are available for this table.
image

You can also add functionality here. Select Insert or Read or Update or Delete and change the code based on your needs (You might want Read() to only return X number of results, sorted in one way ++).

Ok, let’s add data to this table. Open your solution in Visual Studio 2012 and add the two fucntions below (modify to make it fit your table in Azure):

The getScoreBoardfromAzure funtion gets all results that fits the current GameMode (could be a spesific Level also), ordered by Score, and take the top 10. Once this function is done, it adds the results to a list.

   1: getScoreBoardFromAzure: function () {

   2:     var _that = this;

   3:     this.scoreTable.where({ GameMode: this.gameMode })

   4:         .orderByDescending("Score").take(10)

   5:         .read()

   6:         .done(function (results) {

   7:             _that.scoreItems = new WinJS.Binding.List(results);

   8:         });

   9: },

The insertScoreToAzure function adds an item to the table in Windows Azure. Once a new entry is added, update the table in your local client.

   1: insertScoreToAzure: function (scoreItem) {

   2:     var _that = this;

   3:     this.scoreTable.insert(scoreItem).done(function () {

   4:         _that.getScoreBoardFromAzure();

   5:     });

   6: },

Go to the code where you want a new entry to Windows Azure to be added and call the function above like this (change so it fits your table):

   1: this.insertScoreToAzure({

   2:     Name: this.playerName,

   3:     Score: this.score,

   4:     GameMode: this.gameMode

   5: });

Go back to Windows Azure and see your new data in action!

Make sure to crate a screen that renders the content of the list that contains the data the server returns to you when needed, or have a seperate page show it, or the snap view mode of the game.

So, this is how I imlemented the first version of my Online Leaderboards hosted in Windows Azure for Lumilight.

5 – Get the game

The game is NOT YET updated with the Online Leaderboards but the game can be found for free in Windows Store. If you install it, you will get a notification/update when the newest (Release 3) version of the game is out.

Get the game now!

You can also follow Wilhelmsen Studios on facebook to get the latest news.

Posted in Uncategorized | 2 Comments

HTML5 Game Starter Kit for Windows 8

image

Want to create a game for Windows 8? And publish it to the new Windows Store?

Many of the apps that are submitted to the Windows Store are failing certification because they didn’t know that a Privacy Policy was needed, or that the game/app had to implement a snap view and so on. This kit will help you with the most important things.

I have created a simple HTML5 Game starter kit that will help you set up a new Windows 8 game project in short time.

For a game te be certified in Windows Store it must contain a given set of functionality like:
- Minimum resolution: 1024 x 768
- Snap view
- Implement the Settings charm with an About page and a Privacy Policy page.
- Tiles in various sizes.

By using this starter kit you can get most of this functionality ready,  just change the text and the images you want to show!

Updates

Version 0.1
This is the first release. It contains the most important functionalities to get you started. Also an example game comes with it (not really a part of the Starter Kit) just to show it runs and that you can see how Touch is working and so on.

 

How to use the HTML5 Game Stater Kit
It’s very simple to get started.

You can either read how to use this below by following the 5 steps, or see this video:

If you want to learn how to create the example game that comes with this starter kit, you can read the guide here:

Part I:
http://digitalerr0r.wordpress.com/2012/09/19/html5-game-development-for-windows-8-1-getting-started/

Part II:
http://digitalerr0r.wordpress.com/2012/09/20/html5-game-development-for-windows-8-2-adding-a-player/

Part III:
http://digitalerr0r.wordpress.com/2012/09/20/html5-game-development-for-windows-8-3-finishing-your-first-game/

 

Step 1a. You need to have Visual Studio 2012 installed on a Windows 8 device to use this. If you are a student and have access to Dreamspark.com (MSDNAA) or a MSDN Subscription you can download both products from there.

You can use the free version of Visual Studio 2012 (express) and can be downloaded here:
http://www.microsoft.com/visualstudio/eng/downloads

The Release Preview can be downloaded for free here:
http://windows.microsoft.com/en-US/windows-8/release-preview

 

Step 1b. Download HTML5 Game Starter Kit for Windows 8

Step 2. Start Visual Studio 2012 and create a “Blank App” Windows 8 JavaScript project:

Click on File->New->Project…

image

Choose JavaScript as the language and the template “Blank App”:
image

Give your project a name (here: Mitt Spill) and press OK.

A new project is generated and the structure will look like this:
image

 

Step 3. What we will do now is to add the HTML5 Game Starter Kit files to the newly created project. We just copy the content of the HTML5 Game Starter Kit folder to the project folder.

So, copy thse files from the HTML5 Game Starter Kit:
image

Navigate to your new game soludtion and open the project folder. Paste the files here, and replace if asked:

image

The project folder will look somewhat like this:
image

Step 4. Go back to your Visual Studio 2012 project and update if needed:

image

Step 5. Include the new files in your project.
The last thing you need to do is to include the new files in your project structure from Visual Studio 2012.

Click on the button highlighted in the red circle below. It will show the files that exist in the filestructure but not in the project structure(dark gray).

image

Select the following files (hold control and click them):
image

Right click one of the files and select “Include in project”:
image

6. Test if it works.
Congratulations, you are now having a working game project! Run the app and test that it works.

 

Whats in the starter kit?

smalllogostorelogologowidelogo

Tiles

 

image
Full screen mode

image
Snap view mode

image
Full screen with other app in snap view.

image
About page

image
Privacy Policy

Remember!   The example game is using CreateJS. It’s located under js/CreateJS. You can remove this folder if it’s not needed in your project. But if you do so, the example game will not compile.

Posted in Game programming, Windows 8 | 15 Comments

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

Posted in Game programming, Tutorial, Windows 8 | 9 Comments

HTML5 Game Development for Windows 8 #2: Adding a player

image

Hi, and welcome back to the HTML5 game programming tutorial series. In this tutorial we will continue on the previous tutorial, so if you havent done so yet, now is the time! :)

Note: This was written on the plane to Las Vegas in a tired condition. Will fix typos at a later stage.

Last time, we got the main menu up and running. Now we are going to make the main menu screen wait for a touch input, and when that happens, it will start the game and a player is rendered.

The first thing we need is a boolean that tells if we are going to show the main menu or not.

Define the newGame global variable:

   1: var canvas, context;

   2: var gameStage;

   3: var preload;

   4:  

   5: var logoScreenImage, logoScreenBitmap;

   6:  

   7: var newGame = true;

The newGame variable will be used to let our stage know what elements it should display. When we are in the main menu, we hide the game elements and only show the main menu screen.

This will be handled in the update() function (remeber, this is where the game logic is handled). You can use the visible-property of the Bitmap you want to control to make it visible or invisible:

   1: function update() {

   2:     if (newGame) {

   3:         logoScreenBitmap.visible = true;

   4:     }

   5:     else {

   6:         logoScreenBitmap.visible = false;

   7:     }

   8: }

New, the last thing we need in order to acctually change the scene is to wait for the user to touch the screen so the game can start:

In the initialize() function, after the context = canvas.getContext(“2d”); line of code, add the following line:

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

This function creates an event listener that will trigger when the user lifts up his finger (or release the mouse pointer). When this triggers, the function pointerUp is executed.

Let’s take a look at the pointerUp function:

   1: function pointerUp(event) {

   2:     if (newGame) {

   3:         newGame = false;

   4:     }

   5:     else {

   6:     }

   7: }

This function sets the newGame boolean to false when it triggers a new release of the funger/mouse pointer.

Now it’s time to add the player character and the ghost house floor. To do this, we need a few global variables and a class that contains the player logic.

Add the following global variables:

   1: var player;

   2:  

   3: var floorImage, floorBitmap;

   4: var playerIdleImage, playerIdleBitmap;

We also need a variable that will help us keep track of scaling. When the game is deployed to various devices, it’s important that we scale the playfield to make sure it works on all devices.

All the games GFX is made with the 1366 x 768 resolution as the basis. If the resolution changes, we also want to make sure to scale everything. To do this we create two variables :

   1: var scaleW = window.innerWidth / 1366;

   2: var scaleH = window.innerHeight / 768;

 

Now, let’s make sure that the main menu screen scales.

Add the lines with the scale properties to your loadContent() function, where you define and load the mainMenuBitmap:

   1: logoScreenBitmap.scaleX = scaleW;

   2: logoScreenBitmap.scaleY = scaleH;

Also, load the floor texture and the playerIdle texture:

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

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

   3: logoScreenBitmap.scaleX = scaleW;

   4: logoScreenBitmap.scaleY = scaleH;

   5: gameStage.addChild(logoScreenBitmap);

   6:  

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

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

   9: floorBitmap.visible = false;

  10: floorBitmap.scaleX = scaleW;

  11: floorBitmap.scaleY = scaleH;

  12: gameStage.addChild(floorBitmap);

  13:  

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

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

  16: playerIdleBitmap.visible = false;

  17: playerIdleBitmap.scaleX = scaleW;

  18: playerIdleBitmap.scaleY = scaleH;

  19: gameStage.addChild(playerIdleBitmap);

 

We load the needed graphics, and also set the visibility properties to invisible, making sure we don’t draw them on the main menu.

Also, update the update() function so it hides/shows player and floor:

   1: function update() {

   2:     if (newGame) {

   3:         logoScreenBitmap.visible = true;

   4:         playerIdleBitmap.visible = false;

   5:         floorBitmap.visible = false;

   6:     }

   7:     else {

   8:         logoScreenBitmap.visible = false;

   9:         playerIdleBitmap.visible = true;

  10:         floorBitmap.visible = true;

  11:     }

  12: }

 

If you run the game now, you will see the main screen showing. Tapping this will make the main screen go away and you will get in to the playfield.

The player needs a position where it will be rendered, and a target to where he is heading. Also, we need to keep track of the bitmap size in order to calculate collision detection later:

   1: // The player class

   2: function Player() {

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

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

   5:  

   6:     this.targetX = this.positionX;

   7:     this.targetY = this.positionY;

   8:  

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

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

  11: }

Let’s make sure we can move our guy. We want to make him move to the last location the user eiter touched or moved the finger (mousepointer).

In the prepareStage function we create a new instance of the player, add this line above the Ticker.setInterval line of code:
player = new Player();

To make the player move you could either create a function in the player class that moves it based on his target, or put the logic in the update function. I will do both, the player class will get updated directly in the update() function, and the ghosts will be moved with a function on the ghost class level.

At every frame, we check if we should move our guy. It his position is different than the target, move it towards the target. One way to do this is to use a vector, or by directly checking the X and Y values of the position. I will do the latter:

   1: function update() {

   2:     if (newGame) {

   3:         logoScreenBitmap.visible = true;

   4:         playerIdleBitmap.visible = false;

   5:         floorBitmap.visible = false;

   6:     }

   7:     else {

   8:         logoScreenBitmap.visible = false;

   9:         playerIdleBitmap.visible = true;

  10:         floorBitmap.visible = true;

  11:  

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

  13:             player.positionX += 3;

  14:         }

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

  16:             player.positionX -= 3;

  17:         }

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

  19:             player.positionY += 3;

  20:         }

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

  22:             player.positionY -= 3;

  23:         }

  24:     }

  25: }

 

Now, we add a few more event listeners that will make our character move:

   1: function initialize() {

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

   3:     canvas.width = window.innerWidth;

   4:     canvas.height = window.innerHeight;

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

   6:  

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

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

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

  10:  

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

  12:  

  13:     loadContent();

  14: }

 

Let’s start by updating the pointerUp function. We need it to take in a parameter that will contain the touch position, and set the players target to this:

   1: function pointerUp(event) {

   2:     if (newGame) {

   3:         newGame = false;

   4:     }

   5:     else {

   6:         player.targetX = event.x;

   7:         player.targetY = event.y;

   8:     }

   9: }

Also, lets add the two others as well:

   1: function pointerDown(event) {

   2:     if (newGame) {

   3:     }

   4:     else {

   5:         player.targetX = event.x;

   6:         player.targetY = event.y;

   7:     }

   8: }

   9:  

  10: function pointerMove(event) {

  11:     if (newGame) {

  12:     }

  13:     else {

  14:         player.targetX = event.x;

  15:         player.targetY = event.y;

  16:     }

  17: }

 

The last thing we need to do is to update the player image to be rendered at the player positon. Add the following lines in the update function, below the code that makes the player move:

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

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

We want the center of the guy to move towards the location of the finger, not the top-left. thats why we subtract half of the height and width of the character.

The update() function should look like this:

   1: function update() {

   2:     if (newGame) {

   3:         logoScreenBitmap.visible = true;

   4:         playerIdleBitmap.visible = false;

   5:         floorBitmap.visible = false;

   6:     }

   7:     else {

   8:         logoScreenBitmap.visible = false;

   9:         playerIdleBitmap.visible = true;

  10:         floorBitmap.visible = true;

  11:  

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

  13:             player.positionX += 3;

  14:         }

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

  16:             player.positionX -= 3;

  17:         }

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

  19:             player.positionY += 3;

  20:         }

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

  22:             player.positionY -= 3;

  23:         }

  24:  

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

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

  27:     }

  28: }

 

If you run the app now, you should be able to move the guy!

Below is the entire source for this 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:  

  19:     var newGame = true;

  20:  

  21:     var player;

  22:  

  23:     var scaleW = window.innerWidth / 1366;

  24:     var scaleH = window.innerHeight / 768;

  25:  

  26:  

  27:     app.onactivated = function (args) {

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

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

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

  31:                 // your application here.

  32:             } else {

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

  34:                 // Restore application state here.

  35:             }

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

  37:         }

  38:     };

  39:  

  40:     // The player class

  41:     function Player() {

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

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

  44:  

  45:         this.targetX = this.positionX;

  46:         this.targetY = this.positionY;

  47:  

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

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

  50:     }

  51:  

  52:  

  53:     function pointerUp(event) {

  54:         if (newGame) {

  55:             newGame = false;

  56:         }

  57:         else {

  58:             player.targetX = event.x;

  59:             player.targetY = event.y;

  60:         }

  61:     }

  62:  

  63:     function pointerDown(event) {

  64:         if (newGame) {

  65:         }

  66:         else {

  67:             player.targetX = event.x;

  68:             player.targetY = event.y;

  69:         }

  70:     }

  71:  

  72:     function pointerMove(event) {

  73:         if (newGame) {

  74:         }

  75:         else {

  76:             player.targetX = event.x;

  77:             player.targetY = event.y;

  78:         }

  79:     }

  80:  

  81:     function initialize() {

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

  83:         canvas.width = window.innerWidth;

  84:         canvas.height = window.innerHeight;

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

  86:  

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

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

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

  90:  

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

  92:  

  93:         loadContent();

  94:     }

  95:  

  96:     function loadContent() {

  97:         preload = new createjs.PreloadJS();

  98:         preload.onComplete = prepareStage;

  99:  

 100:         var manifest = [

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

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

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

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

 105:         ];

 106:  

 107:         preload.loadManifest(manifest);

 108:     }

 109:  

 110:     function prepareStage() {

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

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

 113:         logoScreenBitmap.scaleX = scaleW;

 114:         logoScreenBitmap.scaleY = scaleH;

 115:         gameStage.addChild(logoScreenBitmap);

 116:  

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

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

 119:         floorBitmap.visible = false;

 120:         floorBitmap.scaleX = scaleW;

 121:         floorBitmap.scaleY = scaleH;

 122:         gameStage.addChild(floorBitmap);

 123:  

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

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

 126:         playerIdleBitmap.visible = false;

 127:         playerIdleBitmap.scaleX = scaleW;

 128:         playerIdleBitmap.scaleY = scaleH;

 129:         gameStage.addChild(playerIdleBitmap);

 130:  

 131:         player = new Player();

 132:  

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

 134:         createjs.Ticker.addListener(gameLoop);

 135:     }

 136:  

 137:     function gameLoop() {

 138:         update();

 139:         draw();

 140:     }

 141:  

 142:     function update() {

 143:         if (newGame) {

 144:             logoScreenBitmap.visible = true;

 145:             playerIdleBitmap.visible = false;

 146:             floorBitmap.visible = false;

 147:         }

 148:         else {

 149:             logoScreenBitmap.visible = false;

 150:             playerIdleBitmap.visible = true;

 151:             floorBitmap.visible = true;

 152:  

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

 154:                 player.positionX += 3;

 155:             }

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

 157:                 player.positionX -= 3;

 158:             }

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

 160:                 player.positionY += 3;

 161:             }

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

 163:                 player.positionY -= 3;

 164:             }

 165:  

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

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

 168:         }

 169:     }

 170:  

 171:     function draw() {

 172:         gameStage.update();

 173:     }

 174:  

 175:     app.oncheckpoint = function (args) {

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

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

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

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

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

 181:         // args.setPromise().

 182:     };

 183:  

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

 185:  

 186:     app.start();

 187: })();

 188:  

In the next tutorial, we will finish the game. This includes handling all the ghosts and keeping track of the score, and a game over function.

image

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

Posted in Game programming, Tutorial, Windows 8 | 5 Comments

HTML5 Game Development for Windows 8 #1: Getting started

image

Hi and welcome to the first part of the HTML5 Game Development for Windows 8 tutorial series.

Note: This was written on the plane to Las Vegas in a tired condition. Will fix typos at a later stage.

This tutorial will cover how you can get started by creating a working game, and requires that you have Windows 8 installed, with Visual Studio 2012.

Also, to aid the development, we will use a library called CreateJS. This is a very powerfull library that helps you with common tasks that’s needed when creating rich interactive apps like games.

Let’s get started!

Start Visual Studio 2012 and click File->New->Project, and create a new blank Windows Store JavaScript app:
image

Give it a name and click OK to generate a new project based on the Blank template.

The project now contains a lot of different files in a project tree. The first folder is css, containing the apps css stylesheet. It’s quite empty, consisting only of an empty body tag and elements for handling the different layout-modes the app can have.

image

The next folder is the images. It contains the default tile-graphics for the app (change these to modify how the app tile looks like).
image

We will use this folder to include more images when we create the game.

Next we have the js folder. This includes the JavaScript files we want to use in the app.

image

By default this contains one file called “default.js”, it contains the basic startup code-behind for the app.

Last we have the default.html file that contains the markup for the app. Change this to add more content and controlls.

First of all, we want to include the CreateJS library files. In this project, we only need two libraries: PreloadJS and EaselJS. Download these from CreateJS.org.

While these download, create a subfolder in “js” called CreateJS:

image

Unzip the two newly downloaded CreateJS libraries. In the filestructure of each library you will find a lib folder containing a js:

lib/easeljs-0.5.0.min.js
lib/preloadjs-0.2.0.min.js

Copy these two files into the CreateJS folder. Now back in Visual Studio, click on the “Show All Files” icon:
image

Now you can see the entire filesctructure, including the two library files you copied earlier. Rightclick on these and include them in the project:

image

image

Next, we will need to include these two libraries in the default.html file. Open this and you will see the following default code:

   1: <!DOCTYPE html>

   2: <html>

   3: <head>

   4:     <meta charset="utf-8" />

   5:     <title>App3</title>

   6:  

   7:     <!-- WinJS references -->

   8:     <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />

   9:     <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
   1:  

   2:     <script src="//Microsoft.WinJS.1.0/js/ui.js">

   1: </script>

   2:  

   3:     <!-- App3 references -->

   4:     <link href="/css/default.css" rel="stylesheet" />

   5:     <script src="/js/default.js">

</script>

  10: </head>

  11: <body>

  12:     <p>Content goes here</p>

  13: </body>

  14: </html>

Click and drag the new library files into the <!—AppName references –> section in default.html (or you can type it in yourself):

   1: <!DOCTYPE html>

   2: <html>

   3: <head>

   4:     <meta charset="utf-8" />

   5:     <title>App3</title>

   6:  

   7:     <!-- WinJS references -->

   8:     <link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />

   9:     <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
   1:  

   2:     <script src="//Microsoft.WinJS.1.0/js/ui.js">

   1: </script>

   2:  

   3:     <!-- App3 references -->

   4:     <link href="/css/default.css" rel="stylesheet" />

   5:     <script src="/js/default.js">

   1: </script>

   2:     <script src="js/CreateJS/easeljs-0.5.0.min.js">

   1: </script>

   2:     <script src="js/CreateJS/preloadjs-0.2.0.min.js">

</script>

  10: </head>

  11: <body>

  12:     <p>Content goes here</p>

  13: </body>

  14: </html>

Now, the libraries are included and ready to ait your quest for game programming! Smilefjes

PreloadJS is a library that will help us load content like images that will be used in the app.

Making the game "Escape!”

Escape is a very simple game where you control a guy trapped in a ghost house. The objective is simple. Stay alive as long as possible! You must control the guy so he doesnt crash into the ghosts. You get points as long as you are awake!

So creating a game requires you to have some graphics. You can download a .zip file containing what you need for this tutorial here.

It contains the images our game will use.

First of all, we need the apps main screen:

LogoScreen_thumb2

The next thing we need is a floor. The game is using a top-down view:

floor_thumb1

Then we need the player:

PlayerIdle_thumb1

And the ghosts that will chase you:

Ghost_thumb1

And thats it for the graphics! Under the images folder in the project tree, add a new folder called “GFX":

image_thumb

And in the same way as we did earlier, copy the imagefiles in to the GFX folder and include them in the project.

image_thumb1

Creating the game loop
The game loop in games is quite different that what you normally have in a game project. We will need to create a function that will initialize the app, then a function that will load the content and prepare everything, and then lastly enter a loop that renders and updates frames every second.

The default.js file will contain the game loop. Initially the default.js code will look like this:

   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:     app.onactivated = function (args) {

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

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

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

  15:                 // your application here.

  16:             } else {

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

  18:                 // Restore application state here.

  19:             }

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

  21:         }

  22:     };

  23:  

  24:     app.oncheckpoint = function (args) {

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

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

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

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

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

  30:         // args.setPromise().

  31:     };

  32:  

  33:     app.start();

  34: })();

 

Let’s start by adding the nessesary functions required by the game loop. I will follow the structure that I’m used to from programming directx and XNA.

Add the following lines into the default.js file, just above the app.oncheckpoint = function (args) { line of code.

   1: function initialize() {

   2: }

   3:  

   4: function loadContent() {

   5: }

   6:  

   7: function gameLoop() {

   8: }

   9:  

  10: function update() {

  11: }

  12:  

  13: function draw() {

  14: }

The entire default.js file now looks like this:

   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:     app.onactivated = function (args) {

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

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

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

  15:                 // your application here.

  16:             } else {

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

  18:                 // Restore application state here.

  19:             }

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

  21:         }

  22:     };

  23:  

  24:     function initialize() {

  25:     }

  26:  

  27:     function loadContent() {

  28:     }

  29:  

  30:     function gameLoop() {

  31:     }

  32:  

  33:     function update() {

  34:     }

  35:  

  36:     function draw() {

  37:     }

  38:  

  39:     app.oncheckpoint = function (args) {

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

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

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

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

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

  45:         // args.setPromise().

  46:     };

  47:  

  48:     app.start();

  49: })();

Now, after initialize is done doing his work, loadContent should start. Once all the content is loaded, we will start the game loop. The gameLoop() will first call the update function, and then call the draw() function. update() will contain the game logic, and draw will handle the drawing.

Update the gameLoop() function to call update() and draw():

   1: function gameLoop() {

   2:     update();

   3:     draw();

   4: }

The last thing we need to do here before creating a drawable canvas is to make sure that our game loop starts once the OS has booted up our app and got the system ready to run it. We do this by using the DOMContentLoaded event. Once the app is ready, it will call the initialize function.

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

   1: app.oncheckpoint = function (args) {

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

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

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

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

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

   7:     // args.setPromise().

   8: };

   9:  

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

  11:  

  12: app.start();

 

 

Creating a drawable surface using the Canvas

Before we can do any drawing, we must add a canvas that we will use to draw on. The canvas will fill the entire screen width and height, making this into a fullscreen app.

Open “default.js” and replace the line <p>Content goes here</p> in the <body> tag with <canvas id="gameCanvas"></canvas>:

   1: <body>

   2:     <canvas id="gameCanvas"></canvas>

   3: </body>

This gives us a canvas with the id gameCanvas. We can use gameCanvas to reference to this canvas from default.js. But how do we draw to it?

Move back to the default.js file and add a few global variables, put these somewhere in the top of the file like below the line var activation = Windows.ApplicationModel.Activation; :

   1: var canvas, context;

Next we need to create a reference to the canvas element gameCanvas and grab the context to it.

This is done in the initialize() function. Make it look like this:

   1: function initialize() {

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

   3:     canvas.width = window.innerWidth;

   4:     canvas.height = window.innerHeight;

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

   6: }

 

This gets the element gameCanvas, sets the resolution to fill the entire screen and gets the 2d context of it.

Next, we need a stage for our game. A stage is class that comes with CreateJS and helps you create “your world”. It needs a reference to the canvas it will draw to and is used to add items like images and soundeffects to your game.

Add a new variable called gameStage as a global variable:

   1: var canvas, context;

   2: var gameStage;

Below the code that gets the canvas and context, we will create the stage object:

   1: function initialize() {

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

   3:     canvas.width = window.innerWidth;

   4:     canvas.height = window.innerHeight;

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

   6:  

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

   8: }

 

Loading images

Now that we got a canvas and a stage to draw to, we can start loading images and drawing them in the canvas.

Once the initialize() function is done, it must call loadContent. This function will use PreloadJS to load images, and create objects that you can insert on your gameStage.

Make initialize() call the loadContent() function:

   1: function initialize() {

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

   3:     canvas.width = window.innerWidth;

   4:     canvas.height = window.innerHeight;

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

   6:  

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

   8:  

   9:     loadContent();

  10: }

Before we can load anything, we need to create a new global variable called “preload”.

   1: var canvas, context;

   2: var gameStage;

   3: var preload;

In loadContent(), we create an instance of the PreloadJS class:

   1: function loadContent() {

   2:     preload = new createjs.PreloadJS();

   3: }

The PreloadJS library works this way. It will need a function that it will call once all the content has been loaded. It will also need a manifest array that contains all the filenames it will load.

   1: function loadContent() {

   2:     preload = new createjs.PreloadJS();

   3:     preload.onComplete = prepareStage;

   4:  

   5:     var manifest = [

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

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

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

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

  10:     ];

  11:  

  12:     preload.loadManifest(manifest);

  13: }

Once the manifest has been loaded, a function called prepareStage will be called. In this function I will simply use the preload object to get images and create instances of them.

Once the stage is ready, we can start the gameLoop() function.

To add images to the stage, we must create a few more global variables:

   1: var canvas, context;

   2: var gameStage;

   3: var preload;

   4:  

   5: var logoScreenImage, logoScreenBitmap;

The logoScreenImage will containg the file, and the logoScreenBitmap will contain the image the file contained. Create we create a new function called prepareStage. To load an image and insert it into the canvas, we use the preload instance and grab what we want from it:

   1: function prepareStage() {

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

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

4: gameStage.addChild(logoScreenBitmap);

   5: }

 

Now, the stage contains one image and it’s default located at the position 0,0 (top-left corner of the screen. When you render an image, the top-left corner of the image is rendered at the given coordinate, not the center of the image.

image

The final part of our rendering engine is to make sure that the gameLoop function ticks at a framerate that good enough for a game.

To do this, we can use the Ticker-class from CreateJS. It will help us with the timing of our game.

   1: function prepareStage() {

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

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

   4:     gameStage.addChild(logoScreenBitmap);

   5:  

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

   7:     createjs.Ticker.addListener(gameLoop);

   8: }

This creates a new interval. At each tick of this interval, the gameLoop function is called. the windows.requestAnimationFrame makes sure that we only draw the frames that we need to draw.

Now, the final thing we need to do to render our stage is to update our draw() function so it updates the stage:

   1: function draw() {

   2:     gameStage.update();

   3: }

 

And thats it!

Now you got a working game engine template. You should save this away as a different project, so you wont need to do this entire thing again when you create a new game. Smilefjes

If you compile and run your app, you will first see the splashscreen and then the main screen image rendered accross the entire screen (but it will not scale to all resolutions yet. More on this in the next tutorial).

image

Listing 1 – the entire default.js file containing the engine that makes our game go around and around:

   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:  

  17:     app.onactivated = function (args) {

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

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

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

  21:                 // your application here.

  22:             } else {

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

  24:                 // Restore application state here.

  25:             }

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

  27:         }

  28:     };

  29:  

  30:     function initialize() {

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

  32:         canvas.width = window.innerWidth;

  33:         canvas.height = window.innerHeight;

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

  35:  

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

  37:  

  38:         loadContent();

  39:     }

  40:  

  41:     function loadContent() {

  42:         preload = new createjs.PreloadJS();

  43:         preload.onComplete = prepareStage;

  44:  

  45:         var manifest = [

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

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

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

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

  50:         ];

  51:  

  52:         preload.loadManifest(manifest);

  53:     }

  54:  

  55:     function prepareStage() {

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

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

  58:         gameStage.addChild(logoScreenBitmap);

  59:  

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

  61:         createjs.Ticker.addListener(gameLoop);

  62:     }

  63:  

  64:     function gameLoop() {

  65:         update();

  66:         draw();

  67:     }

  68:  

  69:     function update() {

  70:     }

  71:  

  72:     function draw() {

  73:         gameStage.update();

  74:     }

  75:  

  76:     app.oncheckpoint = function (args) {

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

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

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

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

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

  82:         // args.setPromise().

  83:     };

  84:  

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

  86:  

  87:     app.start();

  88: })();

 

In the next tutorial, we will start on the logic of the game. This means implementing the game itself, input and handling of the player and enemies.

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

Posted in Game programming, Tutorial, Windows 8 | 9 Comments