Develop Your First Game in Canvas From Start to Finish

Whether you're taking part in the discussion, or reading articles about it, you must be aware that, even though it's still a specification, HTML5 is ready to be used...right now.

It not only improves the semantic structure of the web, but it also brings new JavaScript API's to life to make it easier to embed video, audio and even geolocation technologies (which is particularly interesting for mobile web applications) with ease.

That's not where the APIs stop though. They can additionally be used to build games, by making use of the Canvas 2D API. And that's exactly what we're going to do today.


The Concept

The preview image of this article might point you in the direction of what we are going to build, but it still rather vague, right?

To show the potential of the Canvas 2D API, we'll build a game, which has been created in Flash numerous times before: a "catch the falling objects" game.


As you may know, most of those games are always bound to a specific theme, such as catching fruits in your basket while avoiding rotten apples, or catching golden coins while avoiding worthless rocks.

For the sake of keeping things simple and because most of you with a background in webdesign and development already know what RGB stands for, we'll be catching falling blocks with a specific color: red, green or blue.

We will also implement a somewhat unique feature: the color of the basket indicates which blocks you should catch. For example, if the basket is blue, you are only supposed to catch blue blocks.

Now that we've got our concept ready, we must think of a name to bind it all together. At this point, you could make use of your creativity and brainstorm names, like 'Zealof', 'Xymnia' and 'Doogle' - but a better idea is to come up with a name that is related to the game itself.

As the main point of the game is 'catching' 'RGB' colored 'blocks', a good name could be something along the lines of 'RGBlock' or 'CatchBlock'. I chose to call the game 'RGBCatcher', referring to a possible job title for the player of the game.


A Simple Sketch

Whether you're in the initial stages of building a game, or designing your new portfolio website, a simple sketch is very useful for quickly capturing the rough edges of an idea for later usage.

Long before I began to write a single line of code, I first made a sketch of how things should look.

I surrounded objects with arrows to indicate their movement capabilities, and also added colored dots to help me remember that the color of the basket and the blocks are not supposed to be static.

The whole thing should also have some kind of an interface, where the player can read his health (HP) and score (PT) from. Though I did the original sketch with a simple pencil on paper, it looked similar to the image below.


Don't overdo your sketches; you should use it as a tool to capture the rough edges of an idea - not to paint or describe in words exactly each step of how things should look or work. That's what mockups and prototypes are for.


Getting Started

Before we even start writing a single line of JavaScript, we should first get our HTML5 canvas tag ready. canvas is the HTML5 tag which cooperates with the Canvas 2D API. That's right: cooperates.

It's a common misconception that the new HTML5 functionality does not require JavaScript to run. HTML5 is — just like previous versions — a markup language and is not capable of making the web dynamic all on its own.


Organization of our project

Lack of organization is definitely a no-go, so let's create a new directory and call it exactly the same as the name of the game — it will be holding all the required files for our project.

In this directory, you should create a child directory, called 'js', which will hold the 'rgbcatcher.js' file. We'll be writing JavaScript later, too, so it's a wise idea to have a text editor with at least syntax highlighting and line number support for your own comfort.

In the root of the 'RGBCatcher' directory, create a textfile, called 'index' with the regular HTML extension and open it with your favorite text editor — index.html will function as the starting point for our game.

The HTML

There's no fancy things going on with the markup - just the HTML5 doctype, a reference to rgbcatcher.js, and the canvas tag with very limited styling applied should do the trick.

As you can see, the HTML5 doctype is significantly simpler, compared to most of the current main stream doctypes. Among other useful things, this new doctype allows for quicker instantiation of a standards compliant HTML document.


The JavaScript

In the sketch, each instantiated object has a cross in it. Keeping this in mind: we can easily make a list of which function definitions we will need:

  • A health indicator (countable)
  • A score counter (countable)
  • A block (movable)
  • A basket (controlled by the player, also movable)

So why is there only one block in the list, while we can see five in the sketch? Even though JavaScript is prototype based, we can instantiate multiple block instances from one base block definition.


The Object Wrappers

Now that we've got our object list ready, it shouldn't be too hard to create wrappers for them.

Hang on — the block and basket object are both of the type 'movable' (their coordinates can change), so wouldn't it be useful to have a base movable object, which the block and basket object can later inherit from?

The block and basket object are not the only objects which have certain functionality in common — the health indicator and the score counter are both designed to keep track of a specific value: the score and health, respectively.

That's it. Even though they have no functionality at all, our wrappers are done!



Global Definitions

Before we go any further, we need two things: a few global variables which are accessible from any function requiring it, and a global wrapper. This global wrapper functions as a main handler for our game.

In general, global definitions are a bad thing, due to JavaScript having only one single namespace. This makes it easier for variable and function names to collide between various scripts on the same page, but as this game is pretty much supposed to run as a stand-alone, we shouldn't worry too much about it.

All global variables will be put at the top of the rgbcatcher.js file. The global RGBCatcher wrapper will be placed at the bottom.

We also define a new global rand function, which will make it easier for us to grab a random number in a specific range. This will, for example, be used to select a random color from the RGBCatcher.colors array.


The Movable Base Function

Because both the basket and the block object are movables, which means that their coordinates are not constant, we will first write a base object they can inherit from.

The movable base object should only contain functionality, which will be present in both the block and the basket function definition.

The constructor

A constructor is the routine, which is called as soon as a new object is instantiated and before the object has a prototype assigned to it. This makes a constructor particularly useful for initializing object specific variables, which are then available in the scope of the object.

Because the basket and block object will hold different properties, we will write a constructor which accepts an array and then uses that array to set object specific variables.

Updating the Movable object

The update process of a movable object, which consists of the movement and drawing phase, should only occur when a movable object is still alive.

Because the actual movement and drawing process of a movable object probably depends on its inheritor, the base movable object should not define them unless all the inheritors share the same update or drawing method.


The Basket

Now that we've defined the movable object, we only need to write an additional move and draw method for the basket object which inherits from it.

Basket specific variables

Before we start with the basket object's methods, we should first define a few variables specifically related to this object. These variables will be held by the RGBCatcher wrapper.

The basket variable will hold an instance of the basket object. The basketData variable is an array which holds data about the basket, like its height and horizontal movement speed.

The constructor

As the movable base already has the constructor and update method required for the basket function, we're an inheritance away from implementing it.

Resetting the Basket

We want to both center the basket at the bottom of the screen, and change the color of the basket as soon as a new game or level begins.

By dividing the width of our basket by two and subtracting this value from the canvas's width, also divided by two, as the result, we will get a perfectly centered basket.

To position the basket at the bottom of the screen, we set the y coordinate to the basket's height, subtracted from the canvas's height.

Resetting the basket's color is a little bit more difficult.

We use a while loop which will randomly select a color from the public RGBCatcher.color array on each spin.

As soon as a color is picked which does not equal the old color of the basket, the loop stops spinning and a new oldColor is assigned.

Updating the Basket

As the movable object already brings functionality for an update method, we only need to define specific methods to move and draw the basket.

The method which will move the basket, will, depending on the input of the user, add or subtract the horizontal movement speed (basket.xSpeed) from the basket's current x coordinate.

The move method is also responsible for moving the basket back to its latest possible position if the basket moves out of the canvas element its viewport — this requires us to implement a very simple routine for collision detection.

Collision detection should never be implemented by solely checking on whether an object's coordinate matches a specific expression. This might work for graphics which are 1px by 1px, but the graphical view of anything bigger than that will be completely off as there should also be held account with the width and height.

That's why most of the time you'll need to subtract or add the shape its width or height to respectively the x or y coordinate to match the graphical representation of a collision.

Now that the user is able to move its basket, the only thing we still have to do is to write a method which has the ability to draw the basket on the screen — this is where the Canvas 2D API jumps in.

You might think that making use of this API is extremely sophisticated, but really, it is not! The Canvas 2D API is designed to be as simple, but yet as functional as possible.

We only require two Canvas 2D API dependencies: the fillStyle property and the fillRect method.

The fillStyle property is used to define a fill color for shapes.
The value should follow the CSS3 specification for colors — this means you are allowed to use 'black' but also its HEX equivalent, '#000' — but also works with gradients and patterns but we will use a simple fill color.

The fillRect method draws a filled rectangle, by making use of fillStyle as its fill color, at position (x, y) and a defined width and height in pixels.

Note that the required fillRect parameters can be found as properties in an instantiated basket object, so we can just use the this keyword in combination with the name of the parameter.

Final function definition

That's it; we've got our entire basket object up and running.

Well, I wouldn't exactly call it running, so let's set up the game loop and other various core elements of our game, so we can put this basket to use!


The RGBCatcher Wrapper

To actually implement the functions and prototype we just wrote, they have to be instantiated and called somewhere. This is what the main handler of our game is going to handle: instantiating and calling the right things at the right time.

The run method

The public run method of the RGBCatcher wrapper, will initialize the game components (like the Canvas 2D API, an instance of the basket object and so forth).
It will also set up global event listeners for the keyup and keydown event so that current key presses can be easily retrieved from the global keyOn array.

Resetting the game

As we only have one single game related object to reset so far, which is the basket object, we only need to call this object's reset methods to reset the game.

Note that this method should only be called at the start of a new game. Because we do not have any level management yet, for now it's called from the RGBCatcher's run function.

The game loop

The game loop is responsible for updating and redrawing all the objects of a game until a specific event occurs, like the user running out of health.

Game loops come in all sorts of formats and sizes, but the general order of a game loop is checking for input, updating and moving game objects, clearing the screen and then redrawing all the game objects to the screen.


By simply calling the basket object's update method, we already process most of the steps found in the game loop.

The only additional line of code to write is the line which clears the screen. As our screen is the canvas element, the Canvas 2D API is required to achieve this.

Clearing a canvas by making use off the Canvas 2D API is done via the clearRect method. It accepts four parameters: the x and y coordinate to start clearing from and the width and height to clear.

Because we need to clear a rectangle which overlaps the entire canvas, we start at the point (0, 0) and set the height and width to respectively the height and width of the canvas element.

Now that our game loop clears the canvas at each spin, also called a frame, we only need to call the basket object's update method as it handles the move, update and redraw process all by itself.

An entry point

The only thing still missing for performing a first test run of our game, is an entry point for the game loop.

The entry point's sole purpose is to start the game loop and define an interval for each loop its spin — setInterval is exactly what we need.

As you might have noticed, once entered, our game loop runs infinitely. We will definitely change this later, but for now we are still in the early stages of the development process.


Let's Give it a Go!

Now that the master RGBCatcher wrapper and basket object have been defined, we can finally test our premature game!

Because our game depends on the canvas element, we must wait until it's rendered before we call the RGBCatcher its public run method.
This could be done by including the JavaScript file after the markup of the canvas element, but if you want to keep your JavaScript in the head-element, just attach the run method to the window its onload event.

Fire up the index.html file located in the RGBCatcher directory and try to move the basket by making use of the left and right arrow keys.

Also, try to refresh the page a few times to check whether the basket's color is actually changing or not. Don't worry if you see a repeating color — the old color is not being saved on a page refresh.

Clearly it's not very spectacular yet, so feel free to carry on because we are going to add some falling blocks.



The Block

A catch-the-falling-objects game is not really worth playing without falling objects to catch so let's continue and make those blocks fall!

We will start with writing up the code for the block object and in no time you will see red, green and blue blocks falling down your screen.

Block specific variables

Just like the basket object, a block object needs an array filled with data to store specific properties of a block, like its width and vertical movement speed.
This data array goes in the RGBCatcher wrapper.

So why is there, unlike the basket variable, no block variable which will hold an instance of the block object?
Because a level should contain more than just one block to catch.

In fact, we will make use of some sort of a block manager which will take care of managing blocks — more about that later.

The constructor

The constructor of our block object is exactly the same as the basket object's constructor.

Notice how we would have quite some lines of duplicate code if we would not have been making use of a base movable object definition?

Resetting the Block

As we want to prevent that all blocks fall down at exactly the same position, we use the rand function to put a block at a random horizontal position.

Instead of having a resetPosition method, we will call the method which achieves the random positioning initPosition as this method is about initializing the position, not resetting it.

Initialization? Isn't the constructor supposed to do that? It is, so we will only call the initPosition method once, in the constructor.

A block will now automatically initialize its position on instantiation, but it does not yet initialize its color.

First, we must alter the block function's constructor so that, next to initPosition, a call will be made to initColor at instantiation.

For now, this will throw an "Object # has no method 'initColor'" TypeError so lets add the initColor method to the prototype.

Updating a Block

The movable prototype already brings functionality for a collective update method, so yet again that's something we should not be worried about.

Because there's no input required for a block to fall down, the method which makes a block move, is very straight forward: keep adding the vertical fall speed to the current vertical position.

The only thing left to write for our block object is the draw method, which is yet another task for the Canvas 2D API.

To draw our block we must first set a fill color and then we should draw a rectangle with parameters fetched from the block's properties.
Sounds familiair? It should. We did exactly the same trick with the basket object's draw method, remember?

Because the basket and block object are both movables, we'll just move the basket's draw method to the movable prototype so that all inheritors will share this draw method.

Final function definition

Those blocks are not going to fall all by themselves so let's proceed and add block handling to the RGBCatcher wrapper.


Handling the Blocks

Although the basket is able to move without the help of a computer controlled handler, a block is definitely not.

It requires a supervisor or rather a manager, which is, among other things, able to remove blocks from the screen as soon as they hit the ground. This supervisor will find its place in the RGBCatcher master wrapper.


  • Initialize: Initializing variables related specifically to the blocks, like a variable which indicates how many blocks are still on the screen.
  • Reset: Resetting all the initialized variables to their default values.
  • Update blocks: As soon as the initialization is finished, the game loop is entered and we'll start with looping through all the current available blocks and updating them.
  • Check for collision: For each block we should check whether it is currently in a collision or not and perform specific actions on specific collisions.
  • Add blocks if required: When all the blocks have been updated succesfully and collisions have been handled, new blocks should be spawned if this is required.

Block handling specific variables

We require five more variables for handling the blocks. They all go in the RGBCatcher function definition as private properties.

Reset

The RGBCatcher object wrapper already has a resetGame method. We now only have to add default values for the variables defined above, so they are reset at the start of a new game.

Update blocks

The update blocks phase comes down on looping through all the blocks and updating them. It's also responsible for initiating the next phase of the block management which consists of calling a method to handle the collision detection.

Now we are getting somewhere. Each block in the blocks variable is updated by making use of the block object's update method.

When the block has been updated, it will be pushed to the checkCollision method which will then handle collision detection.

Check for collision

Collision detection is one of the harder parts of game development. Underestimating it, might result in unexpected results.

Have you ever come across a game in which you were able to walk through walls and could shoot your enemies without properly aiming at them? Blame the developers who worked on the collision detection!

Those games were probably 3D, but, lucky for us, we only have to worry about two possible dimensions colliding with each other so practically there's a whole dimension less to go wrong with!

In our RGBCatcher game there is only a total of four collisions to worry about and we already dealt with the first two — remember the possible collision between the basket and the right or left bounds of the screen?

The last two collisions involve the falling blocks and whether they hit the basket or the ground.

Before we are checking whether there is an actual collision with the ground or the basket, we'll check whether the block has passed or is currently on the y line on which the basket resides.

As you can see in the image below, basing collision detection solely on the objects their coordinates is a bad thing as the visual representation of the block has already hit the basket before the coordinates match the collision.


When we are sure the block is visually on the same or on a higher y coordinate than the basket, we can safely check whether the x coordinate of the block is in the correct range.

Other than the y coordinate, there's not just one spot on which a block can horizontally collide with the basket. That's why we need to check if a block is in the range of the basket's total width to determine whether we're dealing with a collision or not.

The range of the basket cannot be described in just one static value, so we'll use a very basic algorithm.

This algorithm will determine whether the block's x position is greater or equal to the x position of the basket and whether the block's x position plus its width is lower than the basket's x position plus its width.


Editing and adding some game variables

Before we go on, we should first add and edit a few variables found in the scope of the RGBCatcher wrapper.

The variable to edit is the blockData variable. We'll add a block property called strength — this value will be inflicted to the user's health if the user misses a correctly colored block or if it catches an incorrectly colored block.

On the other hand, the amount of strength will be added to the user's score if the user catches a block which has the same color as the basket.

Two other variables to add are called health and score. These variables will hold objects representing the graphical health bar and score counter later on, but for now they are just simple integer values.

Catching a block

As our game's objective requires the user to only catch blocks, which have the same color as the basket, we'll first compare the block's color to the basket's color.

If they are the same, we've got a correct catch and the score should increase with the block's strength. On the other hand should an incorrectly colored block decrease the user's health by the block's strength.

In both cases the block should disappear from the screen and the blocksOnScreen variable should decrease with one

Missing a block

If the user misses a correctly colored block, the strength of the block should be inflicted to the player's health. However, there's nothing wrong with missing an incorrectly colored block and if that happens the game should just continue without further action.

In opposite to catching a block, a block should not immediately disappear from the screen. It should first fall down the view port of the canvas and then be removed from the blocks array.

Add blocks if required

So how should our addBlock method work?

It first checks whether the amount of required blocks for the current level (blocksPerLevel * level) equals the amount of already spawned (blocksSpawned) blocks.

If it doesn't, a new block object is spawned, it will be added to the blocks array and the blocksSpawned and blocksOnScreen variables will be increased with one.

If it does, an expression is ran to determine if there are still blocks on the screen (blocksOnScreen) which should land somewhere first.
If this is not the case and blocksOnScreen == 0, we're safe to say that the level has been completed.

The addBlock method returns true if there are still blocks to process and false if the correct amount of blocks have been spawned and removed from the screen.


The Info Screen

We could implement the just written functions in the gameloop and start playing immediately, but wouldn't it be a lot nicer to have a title screen which waits on a response of the user instead of immediately starting with blocks falling down the screen?

Let's write up a function which displays a fancy title screen and waits for input from the user — the space bar sounds appropriate.

We could use the Canvas 2D API to render the title screen, but why not just use a HTML tag and use JavaScript to alter it.

Updating the HTML

The title screen will be put in a common div element tagged with an ID of info as it will function as an info screen too, not just for displaying the title screen.

With the dimensions of 200px * 26px, we use an absolute position and simply calculate the 'top' and 'left' margin of the info element to nicely center it above the canvas element.

For the top position we add the top margin of the #canvas element to the total height, minus the height of the info element and divide it by two.
We do the same thing for the left position, except that instead of using the height, we use the width of both the #canvas and the #info element to calculate the correct position.

Now we got our HTML and CSS sorted again, we can return to the much more interesting process of writing the code which displays the actual title screen.

The titleScreen function

The titleScreen function is a child-function (method) of the RGBCatcher function wrapper.
Before we write it up, we need to add two private variables — info and infoScreenChange.

info holds a DOM object which gives us the necessary tools to alter the info element.
The infoScreenChange boolean, which defaults to true, helps us to determine whether the info screen should be updated or not.

Summing it up: if the info screen has not yet been updated (infoScreenChange === true) , do so.
Then just wait for a press on the space bar (keyOn[32]) to hide the info screen, reset the game and enter the game loop.

There should be made some changes to the run method for setting the local info variable is set and hooking setInterval up to the title screen instead of the game loop.

Feel free to give it a try.
Though there are no blocks falling down yet, we now have a title screen which is being displayed before the game starts and disappears as soon as the user presses the space bar.



Level Management

Now we've got a title screen up and running, it's about time to let those blocks fall.

A bit back, you might have noticed the yet undefined local level variable in the RGBCatcher's private addBlock method. This variable is a requisition of the level management routine.

Let's define it together with a new variable called levelUp. With this boolean we are able to determine whether the user has recently completed a level or not.

Looking back at the scheme for block management, we see a loop of updating, collision checking and adding blocks if this is required. As our game already has a spinning loop, which is the game loop, the repeating pattern goes in there.

But what blocks will be updated if no calls are made to the addBlock function?

Adding timer functionality

When a new block should fall down, depends on the blocksSpawnSec variable. We'll add a simple timer to check against so we can determine whether we should call the addBlock function or not.

The variables required to achieve this, are called frameTime and startTime.

frameTime will be set at each spin of the game loop whereas startTime should only be set at the start of a new timer. This first timer should start to run at the start of a new game.

We already have a function called resetGame, which is called at the start of a new game, so we define the startTime variable there. While we are at it, we might as well add a reset for the level variable.

Note that the function we use to retrieve the current time, getTime, returns an integer value representing the current time in milliseconds since 1970.

Now we have an initial startTime value, we can easily find out how much milliseconds have elapsed by simply extracting this value from the current frameTime.

So after calling the updateBlocks method, we check whether the current frame time is greater than the start time plus the seconds after which a new block should be spawned. If this is the case, we reset the timer and make a call to the addBlock function — depending on its return value, appropriate actions will be performed.

Going up a level

If the addBlock function returns false, we can tell that the current level has been completed, so we set the levelUp variable to true.

We also increase the difficulty of the game by slightly decreasing the time between blocks fall (blocksSpawnSec) and slightly increasing the speed at which blocks fall (blockData['ySpeed']). These in- and decrements are part of the game play — feel free to adjust them later if you think these are too low or too high.

As the level has been completed, a few variables should be reset. This routine is not exactly the same as the reset procedure of a game, so writing a fitting function for that is the next thing we'll do.

Resetting a level

Considering the concept of our game, the color and the position of the basket should be reset at the start of a new level. To award the player for completing the level, we will reset its health.

Obviously, the blocksSpawned, blocksOnScreen and blocks variable should also be reset.

Game Over

Now we have a resetLevel implementation, we can continue with working on the game loop.

What's still missing from it is a check on the user's health to determine whether it still has enough health to continue, and a routine which lets the user know that a new level is starting.

The first thing is relatively simple, so let's start with that. Basically, we only have to check if the player's health is lower than 1.

So how should the game-over-procedure be implemented? At the point at which the user has less than 1/100 HP left, the game should end and a game over message should be shown.

This is best accomplished by quitting the current game loop and entering a new game over loop which will flash a message for a specific amount of time, three seconds sounds fine, after which the title screen is shown again.

A new gameOverScreen function should be defined which first clears the entire canvas, displays the game-over-message and after three seconds have elapsed, goes back to the title screen.

The game-over-message will be put on the info screen. Displaying the message for just three seconds is easily achieved by using the timer functionality you should be familiar with now.

Get ready for level 2!

What's missing from our updated game loop is a routine which handles the flashing of a level up message.
We already have a variable called levelUp which helps us to determine whether a message should be shown or not.

So we just copy the gameOverScreen code to the if-statement, change the message to be flashed and the actions performed after the three seconds mark is reached.

Feel free to give the game a go again. It's pretty much functional, except that there are still two things missing: a score counter and a health bar. We are making progress, now!



The Countable Base Function

A score counter and a health bar have one thing in common: they count a specific value. Therefore it's a good idea for them to have an object they can inherit from: a countable base object — just like we did with the movable base definition for the basket and block object.

Starting with the fact that both the health and score object should supply us with a method for changing their value, they should also have a method which returns the current value of the instantiated object and they will both have an update and movement routine.

The constructor

The constructor of the countable base, should only define properties which are shared by all the inheritors.

These are x and y for positioning, the value property which holds the value of a countable (like an amount of health), the targetValue property and the speed property.

The speed and targetValue properties will be used for a graphical animation, but we will come back to that later.

A reset method should be declared by an inheritor which will set the object's properties to custom values for initialization.

Updating the Countable object

The update method of the countable prototype is identical to the update method of the movable prototype except that it does not require an 'is alive' check.

Setting the value of the Countable object

The targetValue and value property will be used to perform a basic graphical animation in which the health bar or score counter slides from its old value to its new targetValue.

This is achieved by not directly setting the value property, but by using the sub-station called targetValue which we can use to set up an animation.

By directly adding the desired amount of change to the target value, we can also use a negative amount. This gives us the ability to subtract, but also add a certain amount to the target value.

Moving the Countable object

The movement process of a countable object is not about changing its coordinates, like we did with the block and basket's move routine, but about changing the current value so it will eventually be the same as targetValue.

Practically this will result in a process of the value property slightly increasing or decreasing in value until it's at the same value as targetValue.

As the value property will be used to draw the score or the health bar, changing it over time instead of setting directly, results in an animation.

We should first determine whether the value is already equal to the target value. If this is the case, nothing should happen. If it isn't, the speed should be added (value < targetValue) or subtracted (value > targetValue) from the actual value.

As soon as the difference between the target and actual value is lower than the animation speed, the value should be set to the target value as adding or subtracting the speed from the value will be more than sufficient.

Final function definition

Now that we have a base Countable function definition, an inheritor only needs to define a reset and draw method... and it's ready to go! How convenient is that?


The Health Object

Obvious as it is, the health object is responsible for keeping track the health points of the user and drawing the health bar to the screen.

As the base countable function already supplies us with an update, change and get method, we only need to define a reset and draw method.

The constructor

Though the value and targetValue properties of this object should be reset in the reset method, the constructor defines the static x and y property.


If we look back at the sketch, we see that we planned to position the health bar in the top right corner with some additional margin from the canvas's bounds.

This is achieved by setting the x property to the canvas's width subtracted by the width of the health bar (52px) and the desired right margin (10px). Secondly, we will set the y property to 10, which is nothing more than the top margin.

We also make a call to the yet to be defined reset method which will set the value and targetValue property at instantiation.

Resetting the Health object

The base countable object already defines a few properties, but as not all the inheritors share the same exact property values, the inheritor its reset method will set those properties to the correct values.

As we want to start the game with an animation of the health bar filling up, we'll set the starting health value at 1 and the target value at 100 instead of leaving them at their default values.

Drawing the Health object

The health bar consists of three collaborating drawings; a container, an actual health bar which its width fluctuates and a small piece of text for the interface.

The container

The container is fairly simple to draw even though it introduces a new method of the Canvas 2D API: the strokeRect method; a method for drawing a rectangle with a border specified by the current stroke style.

strokeRect requires exactly the same parameters as fillRect: x, y, width and height. To define the stroke its color, the Canvas 2D API its strokeStyle property is used. We will leave it at its default value of black.

Just like the fillRect method, a fill color can be defined by setting the fillStyle property.

Note that we add two pixels to both the width and the height of the container. This is to actually make the container 50x5 pixels as the Canvas 2D API puts the border inside the defined width and height.

The health bar


The animation its logic is already handled by the inherited move method of the countable object. In combination with the varying color, this will give changes made to the health percentage a nice transition.

Setting the Canvas 2D API its fillStyle property accordingly to the health percentage is incredibly simply when using a few if statements.

The process of drawing the health bar isn't much more difficult to do; we only have to draw a filled rectangle (fillRect) at the same position of the container, except that we add one pixel to both the width and the height to position it within the borders of the container.

The width of the health bar is simply found by multiplying the current health percentage (value) with the scale ratio of the container its blank space (50) and the possible maximum amount of health (100). 5 pixels should be sufficient as its height.

The 'HP' text

Rendering text to the canvas can be achieved by making use of the fillText or strokeText method of the Canvas 2D API. A font definition can be set with the font property (defaults to '10px sans-serif').

Just like rectangles, the fillStyle and strokeStyle properties are used to respectively set a fill or border color. Other than that, there are also properties for setting the align (textAlign, defaults to 'start') and the baseline (textBaseline, defaults to 'alphabetic') of the text.

We will use the fillText method as we do not want to have a border around our text. fillText requires the following parameters: a piece of text to print and an x and y position.

To simplify the process of putting the text on the right spot, we'll set the text its baseline to 'top'. There's still some padding inserted before the text is being drawn and therefore we will subtract 3 pixels from the y position to put it in align with the health bar.


Final function definition

And with that, we have completed our health object.
The final element for us to put together before we can call it an end is the score object.


The Score Object

The score object inherits from the countable base object which, again, gives us the advantage of having a few methods less to write. This means we only have to write two methods.

One for drawing the text 'PT' (short-hand for points) and the current value to the canvas and one for resetting the object's properties so the player will start fresh at the beginning of a new level.

The constructor

As the position of the score counter is static and is not required to be reset at the start of a new game, it's fine to define it in the constructor.

Whereas the x position should be the same as the health bar to align the interface nicely, the y position should be a bit lower. This comes down to 10 pixels for the top margin, an additional 7 pixels for the health bar its height and another 5 pixels for the margin between the health bar and the score counter.

Resetting the Score object

The reset method of the Score object should only be called at the start of a new game as the score should be taken along with the levels the player completes — setting the value and targetValue properties to zero is sufficient.

Drawing the Score object

The entire drawing phasing of the score object consists of nothing more than putting two pieces of text on the canvas; 'PT' and the current score.

The 'PT' text can be positioned just like we did with the 'HP' text; altering the already defined x property a bit to position it with some whitespace from the actual score counter.

Final function definition

That is that for the score object.
Let's continue to the final stage and actually implement the health and score object into our game.


Putting it All Together

To actually integrate the health and score counter into our game, we should update the master RGBCatcher function wrapper.

Starting with the run method, we should add two lines to it for instantiating a new health and score counter object.

In the resetGame and resetLevel method the lines which set the health and score variable to zero, when they were still simple integer values, should be replaced with a call to the health and score object their reset method.

The gameLoop method its routine to determine whether the user still has enough health to continue playing, still works with the health variable as if it were an integer (health ).

To the gameLoop method there should also be added a call to the health and score objects their update methods. It fits nicely under the call to the basket object's update method.

The last thing we should change are increment and decrement calls to the old health and score integer values in the checkCollision function in the RGBCatcher wrapper.

Those two variables are no longer integers and we should therefore use the health and score object their change method to respectively inflict damage or add points to the score.

And we're done!


Conclusion

By using some common sense and dividing the elements of a game into separate tasks, it's not too difficult to hack up a basic game using HTML5 technologies.

Feel free to go on and continue exploring the marvelous world of the Canvas 2D API. For example, you could try to alter the drawing phase of the basket so it actually has a gap.

You could also think about improving the game by adding blocks, which will increase the user's health or add top scores by making use of HTML5's local storage functionality.

Whatever you do, have fun with it!

Tags:

Comments

Related Articles