O'Reilly logo

HTML5 Canvas by Jeff Fulton, Steve Fulton

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Geo Blaster Game Algorithms

The game source code covers a lot of ground that we did not touch on earlier in this chapter. Let’s discuss some of those topics now; the rest will be covered in detail in Chapter 9.

Arrays of Logical Display Objects

We have used arrays to hold all our logical display objects, and we have an array for each type of object (rocks, saucers, playerMissiles, saucerMissiles, and particles). Each logical display object is a simple object instance. We have created a separate function to draw and update each of our objects.


The use of an object class prototype similar to FrameRateCounter can be implemented easily for the various display object types. To conserve space, we have not implemented them in this game. However, these objects would allow us to separate the update and draw code from the current common functions, and then place that code into the individual object prototypes. We have included a Rock prototype at the end of this chapter as an example (see Example 8-13).

You will notice that saucers and rocks are drawn with points in the same manner as the player ship.


The rocks will be simple squares that rotate clockwise or counterclockwise. The rock instances will be in the rocks array. When a new level starts, these will all be created in the upper-right corner of the game screen.

Here are the variable attributes of a rock object:

newRock.scale = 1;
newRock.width = 50;
newRock.height = 50;
newRock.halfWidth = 25;
newRock.halfHeight = 25;
newRock.scoreValue = bigRockScore;
newRock.rotation = 0;

The rock scale will be set to one of the three rock-scale constants discussed earlier. halfWidth and halfHeight will be set based on the scale, and they will be used in calculations in the same manner as the player object versions. The dx and dy values represent the values to apply to the x and y axes when updating the rock on each frame tick.


Unlike Atari’s Asteroids game, which has both small and large saucers, we are only going to have one size in Geo Blaster Basic. It will be stored in the saucers array. On a 28×13 grid (using paths), it looks like Figure 8-6.

The saucer design

Figure 8-6. The saucer design

The variable attributes of the saucer object are very similar to the attributes of a rock object, although without the rock scale attribute. Also, saucers don’t have a rotation; it is always set at 0. The saucer also contains variables that are updated on each new level to make the game more challenging for the player. Here are those variables, which will be discussed in more detail in the upcoming section :

newSaucer.fireRate = levelSaucerFireRate;
newSaucer.fireDelay = levelSaucerFireDelay;
newSaucer.fireDelayCount = 0;
newSaucer.missileSpeed = levelSaucerMissileSpeed;


Both the player missiles and saucer missiles will be 2×2-pixel blocks. They will be stored in the playerMissiles and saucerMissiles arrays, respectively.

The objects are very simple. They contain enough attributes to move them across the game screen and to calculate life values:

newPlayerMissile.dx = 5*Math.cos(Math.PI*(player.rotation)/180);
newPlayerMissile.dy = 5*Math.sin(Math.PI*(player.rotation)/180);
newPlayerMissile.x = player.x+player.halfWidth;
newPlayerMissile.y = player.y+player.halfHeight;
newPlayerMissile.life = 60;
newPlayerMissile.lifeCtr = 0;
newPlayerMissile.width = 2;
newPlayerMissile.height = 2;

Explosions and particles

When a rock, saucer, or the player ship is destroyed, that object explodes into a series of particles. The createExplode() function creates this so-called particle explosion. Particles are simply individual logical display objects with their own life, dx, and dy values. Randomly generating these values makes each explosion appear to be unique. Particles will be stored in the particles array.

Like missiles, particle objects are rather simple. They also contain enough information to move them across the screen and to calculate their life span in frame ticks:

newParticle.dx = Math.random()*3;
newParticle.dy = Math.random()*3;
newParticle.life = Math.floor(Math.random()*30+30);
newParticle.lifeCtr = 0;
newParticle.x = x;
newParticle.y = y;

Level Knobs

Even though we never show the level number to the game player, we are adjusting the difficulty every time a screen of rocks is cleared. We do this by increasing the level variable by 1 and then recalculating these values before the level begins. We refer to the variance in level difficulty as knobs, which refers to dials or switches. Here are the variables we will use for these knobs:


Number of rocks

levelRockMaxSpeedAdjust = level*.25;

Rock max speed

levelSaucerMax = 1+Math.floor(level/10);

Number of simultaneous saucers

levelSaucerOccurrenceRate = 10+3*level;

Percent chance a saucer will appear

levelSaucerSpeed = 1+.5*level;

Saucer speed

levelSaucerFireDelay = 120-10*level;

Delay between saucer missiles

levelSaucerFireRate = 20+3*level;

Percent chance a saucer will fire at the player

levelSaucerMissileSpeed = 1+.2*level;

Speed of saucer missiles

Level and Game End

We need to check for game and level end so we can transition to either a new game or to the next level.

Level end

We will check for level end on each frame tick. The function to do so will look like this:

function checkForEndOfLevel(){
   if (rocks.length==0) {

Once the rocks array length is 0, we switch the state to GAME_STATE_NEW_LEVEL.

Game end

We do not need to check for the end of the game on each frame tick. We only need to check when the player loses a ship. We do this inside the gameStatePlayerDie() function:

function gameStatePlayerDie(){
   if (particles.length >0 || playerMissiles.length>0) {

      if (playerShips<1) {

This is the state function that is called on each frame tick during the GAME_STATE_PLAYER_DIE state. First, it checks to see that there are no longer any particles on the screen. This ensures that the game will not end until the player ship has finished exploding. We also check to make sure that all the player’s missiles have finished their lives. We do this so we can check for collisions between the playerMissiles, and for rocks against saucers. This way the player might earn an extra ship before playerShips-- is called.

Once the particles and missiles have all left the game screen, we subtract 1 from the playerShips variable and then switch to GAME_STATE_GAME_OVER if the playerShips number is less than 1.

Awarding the Player Extra Ships

We want to award the player extra ships at regular intervals based on her score. We do this by setting an amount of points that the game player must achieve to earn a new ship—this also helps us keep a count of the number of ships earned:

function checkForExtraShip() {
   if (Math.floor(score/extraShipAtEach) > extraShipsEarned) {

We call this function on each frame tick. The player earns an extra ship if the score/extraShipAtEach variable (with the decimals stripped off) is greater than the number of ships earned. In our game, we have set the extraShipAtEach value to 10000. When the game starts, extraShipsEarned is 0. When the player’s score is 10000 or more, score/extraShipAtEach will equal 1, which is greater than the extraShipsEarned value of 0. An extra ship is given to the player, and the extraShipsEarned value is increased by 1.

Applying Collision Detection

We will be checking the bounding box around each object when we do our collision detection. A bounding box is the smallest rectangle that will encompass all four corners of a game logic object. We have created a function for this purpose:

function boundingBoxCollide(object1, object2) {

   var left1 = object1.x;
   var left2 = object2.x;
   var right1 = object1.x + object1.width;
   var right2 = object2.x + object2.width;
   var top1 = object1.y;
   var top2 = object2.y;
   var bottom1 = object1.y + object1.height;
   var bottom2 = object2.y + object2.height;

   if (bottom1 < top2) return(false);
   if (top1 > bottom2) return(false);

   if (right1 < left2) return(false);
   if (left1 > right2) return(false);



We can pass any two of our game objects into this function as long as each contains x, y, width, and height attributes. If the two objects are overlapping, the function will return true. If not, it will return false.

The checkCollision() function for Geo Blaster Basic is quite involved. The full code listing is given in Example 8-12. Rather than reprint it here, let’s examine some of the basic concepts.

One thing you will notice is the use of “labels” next to the for loop constructs. Using labels such as in the following line can help streamline collision detection:

rocks: for (var rockCtr=rocksLength;rockCtr>=0;rockCtr--){

We will need to loop through each of the various object types that must be checked against one another. But we do not want to check an object that was previously destroyed against other objects. To ensure we do the fewest amount of collision checks necessary, we have implemented a routine that employs label and break statements.

Here is the logic behind the routine:

  1. Create a rocks: label and then start to loop through the rocks array.

  2. Create a missiles: label inside the rocks iteration, and loop through the playerMissiles array.

  3. Do a bounding box collision detection between the last rock and the last missile. Notice that we loop starting at the end of each array so that we can remove elements (when collisions occur) in the array without affecting array members that have not been checked yet.

  4. If a rock and a missile collide, remove them from their respective arrays, and then call break rocks and then break missiles. We must break back to the next element in an array for any object type that is removed.

  5. Continue looping through the missiles until they have all been checked against the current rock (unless break rocks was fired off for a rock/missile collision).

  6. Check each saucer, each saucer missile, and the player against each of the rocks. The player does not need a label because there is only a single instance of the player. The saucers and saucerMissiles will follow the same logic as missiles. If there is a collision between one and a rock, break back to their respective labels after removing the objects from their respective arrays.

  7. Once we have checked the rocks against all the other game objects, check the playerMissiles against the saucers using the same basic logic of loop labels, looping backward through the arrays, and breaking back to the labels once objects are removed.

  8. Check the saucerMissiles against the player in the same manner.

Over the years, we have found this to be a powerful way to check multiple objects’ arrays against one another. It certainly is not the only way to do so. If you are not comfortable using loop labels, you can employ a method such as the following:

  1. Add a Boolean hit attribute to each object and set it to false when an object is created.

  2. Loop through the rocks and check them against the other game objects. This time the direction (forward or backward) through the loops does not matter.

  3. Before calling the boundingBoxCollide() function, be sure that each object’s hit attribute is false. If not, skip the collision check.

  4. If the two objects collide, set each object’s hit attribute to true. There is no need to remove objects from the arrays at this time.

  5. Loop though playerMissiles and check against the saucers, and then loop through the saucers to check against the player.

  6. When all the collision-detection routines are complete, reloop through each object array (backward this time) and remove all the objects with true as a hit attribute.

We have used both methods—and variations—on each. While the second method is a little cleaner, this final loop through all of the objects might add more processor overhead when dealing with a large number of objects. We will leave the implementation of this second method to you as an exercise, in case you wish to test it.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required