10 min read

(For more resources related to this topic, see here.)

Let’s get serious – the game

The game we will implement now is inspired by Frogger. In this old school arcade game, you played the role of a frog trying to cross the screen by jumping on logs and avoiding cars.

In our version, the player is a developer who has to cross the network cable by jumping packets and then cross the browser “road” by avoiding bugs. To sum up, the game specifications are as follows:

  • If the player presses the up arrow key once, the “frog” will go forward one step.

  • By pressing the right and left arrow key, the player can move horizontally.

  • In the first part (the network cable) the player has to jump on packets coming from the left of the screen and moving to the right. The packets are organized in lines where packets of each line travel at different speeds. Once the player is on a packet, he/she will move along with it. If a packet drives the player outside of the screen, or if the player jumps on the cable without reaching a packet, he/she will die and start at the beginning of the same level once again.

  • In the second part (the browser part) the player has to cross the browser screen by avoiding the bugs coming from the left. If the player gets hit by a bug he/she will start at the beginning of the same level once again.

These are very simple rules, but as you will see they will already give us plenty of things to think about.

Learning the basics

Throughout this article, we will use DOM elements to render game elements. Another popular solution would be to use the Canvas element. There are plus and minus points for both technologies and there are a few effects that are simply not possible to produce with only DOM elements.

However, for the beginner, the DOM offers the advantage of being easier to debug, to work on almost all existing browsers (yes, even on Internet Explorer 6), and in most cases to offer reasonable speed for games. The DOM also abstracts the dirty business of having to target individual pixels and tracking which part of the screen has to be redrawn.

Even though Internet Explorer supports most of the features we will see in this book, I would not recommend creating a game that supports it. Indeed, its market share is negligible nowadays (http://www.ie6countdown.com/) and you will encounter some performance issues.

Now from some game terminology, sprites are the moving part of a game. They may be animated or nonanimated (in the sense of changing their aspect versus simply moving around). Other parts of the game may include the background, the UI, and tiles.

Framework

During this article, we will write some code; part of the code belongs to an example game and is used to describe scenes or logic that are specific to it. Some code, however, is very likely to be reused in each of your games. For this reason, we will regroup some of those functions into a framework that we will cleverly call gameFramework or gf in short.

A very simple way to define a namespace in JavaScript is to create an object and add all your function directly to it. The following code gives you an example of what this might look like for two functions, shake and stir, in the namespace cocktail.

// define the namespace
var cocktail = {};
// add the function shake to the namespace
cocktail.shake = function(){...}
// add the function stir to the namespace
cocktail.stir = function(){...}

This has the advantage of avoiding collision with other libraries that use similar names for their objects or functions. Therefore, from now on when you see any function added to the namespace, it will mean that we think those functions will be used by the other games we will create later in this article or that you might want to create yourself.

The following code is another notation for namespace. Which one you use is a personal preference and you should really use the one that feels right to you!

var cocktail = {
// add the function shake to the namespace
shake: function(){...},
// add the function stir to the namespace
stir: function(){...}
};

Typically, you would keep the code of the framework in a JS file (let’s say gameFramework.js) and the code of the game in another JS file. Once your game is ready to be published, you may want to regroup all your JavaScript code into one file (including jQuery if you wish so) and minimize it. However, for the whole development phase it will be way more convenient to keep them separate.

Sprites

Sprites are the basic building blocks of your game. They are basically images that can be animated and moved around the screen. To create them you can use any image editor. If you work on OS X, there is a free one that I find has been particularly well done, Pixen (http://pixenapp.com/).

  • You can use animated gifs. With this method you have no way to access the index of the current frame through JavaScript, and no control over when the animation starts to play or when it ends. Furthermore, having many animated GIFs tends to slow things down a lot.

  • You can change the source of the image. This is already a better solution, but provides worse performance if proposed and requires a large number of individual images.

Another disadvantage is that you cannot choose to display only one part of the image; you have to show the entire image each time. Finally, if you want to have a sprite made of a repeating image, you will have to use many img elements.

For the sake of completeness, we should mention here one advantage of img; it’s really easy to scale an img element—just adjust the width and height.

The proposed solution uses simple divs of defined dimensions and sets an image in the background. To generate animated sprites, you could change the background image, but instead we use the background position CSS property. The image used in this situation is called a sprite sheet and typically looks something like the following screenshot:

The mechanism by which the animation is generated is shown in the following screenshot:

Another advantage is that you can use a single sprite sheet to hold multiple animations. This way you will avoid having to load many different images. Depending on the situation, you may still want to use more than one sprite sheet, but it’s a good thing to try to minimize their number.

Implementing animations

It’s very simple to implement this solution. We will use .css() to change the background properties and a simple setInterval to change the current frame of the animation. Therefore, let’s say that we have a sprite sheet containing 4 frames of a walk cycle where each frame measures 64 by 64 pixels.

First, we simply have to create a div with the sprite sheet as its background. This div should measure 64 by 64 pixels, otherwise the next frame would leak onto the current one. In the following example, we add the sprite to a div with the ID mygame.

$("#mygame").append("<div id='sprite1'>"); $("#sprite1").css("backgroundImage","url('spritesheet1.png')");

As the background image is by default aligned with the upper-left corner of the div, we will only see the first frame of the walk-cycle sprite sheet. What we want is to be able to change what frame is visible. The following function changes the background position to the correct position based on the argument passed to it. Take a look at the following code for the exact meaning of the arguments:

/** * This function sets the current frame. * -divId: the Id of the div from which you want to change the * frame * -frameNumber: the frame number * -frameDimension: the width of a frame **/ gameFramework.setFrame = function(divId,frameNumber, frameDimension) { $("#"+divId) .css("bakgroundPosition", "" + frameNumber * frameDimension + "px 0px"); }

Now we have to call this at regular intervals to produce the animation. We will use setInterval with an interval of 60 milliseconds, that is, around 17 frames per second. This should be enough to give the impression of walking; however, this really has to be fine-tuned to match your sprite sheet. To do this we use an anonymous function that we pass to setInterval, which will in turn call our function with the correct parameter.

var totalNumberOfFrame = 4;
var frameNumber = 0;
setInterval(function(){
gameFramework.setFrame("sprite1",frameNumber, 64);
frameNumber = (frameNumber + 1) % totalNumberOfFrame;
}, 60);

You probably noticed that we’re doing something special to compute the current frame. The goal is to cover values from 0 to 3 (as they’re 4 frames) and to loop back to 0 when we reach 4. The operation we use for this is called modulo (%) and it’s the rest of the integer division (also known as Euclidean division).

For example, at the third frame we have 3 / 4 which is equal to 0 plus a remainder of 3, so 3 % 4 = 3. When the frame number reaches 4 we have 4 / 4 = 1 plus a remainder of 0, so 4 % 4 = 0. This mechanism is used in a lot of situations.

Adding animations to our framework

As you can see there are more and more variables needed to generate an animation: the URL of the image, the number of frames, their dimension, the rate of the animation, and the current frame. Furthermore, all those variables are associated with one animation, so if we need a second one we have to define twice as many variables.

The obvious solution is to use objects. We will create an animation object that will hold all the variables we need (for now, it won’t need any method). This object, like all the things belonging to our framework, will be in the gameFramework namespace. Instead of giving all the values of each of the properties of the animation as an argument, we will use a single object literal, and all the properties that aren’t defined will default to some well-thought-out values.

To do this, jQuery offers a very convenient method: $.extend. This is a very powerful method and you should really take a look at the API documentation (http://api.jquery.com/) to see everything that it can do. Here we will pass to it three arguments: the first one will be extended with the values of the second one and the resulting object will be extended with the values of the third.

/**
* Animation Object.
**/
gf.animation = function(options) {
var defaultValues = {
url : false,
width : 64,
numberOfFrames : 1,
currentFrame : 0,
rate : 30
};
$.extend(this, defaultValues, options);
}

To use this function we will simply create a new instance of it with the desired values. Here you can see the values used in the preceding examples:

var firstAnim = new gameFramework.animation({
url: "spritesheet1.png",
numberOfFrames: 4,
rate: 60
});

As you can see, we didn’t need to specify width: 64 because it’s the default value! This pattern is very convenient and you should keep it in mind each time you need default values and also the flexibility to override them.

We can rewrite the function to use the animation object:

gf.setFrame = function(divId, animation) {
$("#" + divId)
.css("bakgroundPosition", "" + animation.currentFrame *
animation.width + "px 0px");
}

Now we will create a function for our framework based on the technique we’ve already seen, but this time it will use the new animation object. This function will start animating a sprite, either once or in a loop. There is one thing we have to be careful about—if we define an animation for a sprite that is already animated we need to deactivate the current animation and replace it with the new one.

To do this we will need an array to hold the list of all intervals’ handles. Then we’ll only need to check if one exists for this sprite and clear it, then define it again.

gf.animationHandles = {}; /** * Sets the animation for the given sprite. **/ gf.setAnimation = function(divId, animation, loop){ if(gf.animationHandles[divId]){ clearInterval(gf.animationHandles[divId]); } if(animation.url){ $("#"+divId).css("backgroundImage","url('"+animation. url+"')"); } if(animation.numberOfFrame > 1){ gf.animationHandles[divId] = setInterval(function(){ animation.currentFrame++; if(!loop && currentFrame > animation.numberOfFrame){ clearInterval(gf.animationHandles[divId]); gf.animationHandles[divId] = false; } else { animation.currentFrame %= animation. numberOfFrame; gf.setFrame(divId, animation); } }, animation.rate); } }

This will provide a convenient, flexible, and quite high-level way to set an animation for a sprite.

LEAVE A REPLY

Please enter your comment!
Please enter your name here