Also, in the name of clarity and ease of explanation, optimization of codes is not done.
If you have gone through the earlier Set Up tutorial, you'll realise that it is the same. In fact, for subsequent tutorials, we will not be making much modifications to this html file.
For this tutorial, we will not be using any sprites, but rather, use the canvas drawing methods to draw rectangles acting as the game sprites. You will just require these 3 simple files as we discussed previously in the Set Up tutorial.
We have covered how to draw a circle at the place where the player clicks in the previous tutorial. Here, we will learn how to draw a rectangle onto the game canvas. We will make them drop from the top of the screen.
We will then demonstrate how the game does a collision detection check whenever you click on the game stage, and award you some points if you successfully clicked on the rectangles.
Step 1 - Making Rectangles Drop From The Top Of The Screen
To do a collision detection between your mouse click and some game objects, we need to create an endless flow of game objects dropping down the screen first. For ease of explanation here, we will just randomly draw some rectangles instead of loading some game sprites. Examine the codes below.
var gameloop, mouseX, mouseY, isClicked, boxArray, score;
isClicked = false;
score = 0;
//Setup the rectangles Array
boxArray = new Array();
In line 36, we declare a whole list of variables which we will be using for the purpose of this tutorial. In particular, we will need a new class called an Array. Line 43 shows the instantiation of that object, and it is stored inside boxArray. Later, whenever we create new rectangles to fall from the top of the game stage, we will push it into this array.
Within the game loop, the codes below show how the rectangles are created.
//Check if we should generate a new Rectangle to drop down
if (Math.random() < 0.02)
var newBox = new Object();
newBox.x = Math.floor(Math.random() * stage.width);
newBox.y = -10;
In line 73, we use an in-built function called Math.random(), which will generate a number from 0 to 1 (exclusive, i.e. will never include 1). This means that anything from 0.01 to 0.9999999 is possible, and with "equal" probability. When we compare it to "<0.02", we're actually saying everytime this code executes, there is a 2% chance of lines 74 to 79 happening.
And when that happens, line 75 will create a new object. When we say "new Object()", we actually do not create an object of any specific class. The beauty here is that we can dynamically add new attributes to this object, as you will see in lines 76 and 77. We further randomise the x position of that rectangle (so they do not always drop from the same place), and set the y position to be -10 (so that they appear further away from view first).
Let's skip ahead and look at the drawRect function which you find in lines 112 to 120. This function is called whenever you need to display to the player where the rectangles are. The function only takes in both the x and y coordinates of the rectangle. If you would like to modify it in future to vary the colour or size of the rectangles, you can just include more parameters to take in.
function drawRect(xPos, yPos)
ctx.rect(xPos,yPos, BOX_WIDTH, BOX_HEIGHT);
ctx.fillStyle = "white";
ctx.strokeStyle = 'black';
In line 114, we prepare the game to start drawing the rectangle by calling beginPath(). Then we call the function rect(...) and pass in both the x and y position first. The next two parameters are the width and height of the rectangles, or boxes, and we have specified these constants BOX_WIDTH and BOX_HEIGHT in the constants.js file.
Lines 116 to 117 fills the rectangle with a shade of white, and lines 118 to 119 draws a black line around it.
Step 2 - Updating The Position Of The Rectangles
Let us now go back to the game loop where we left off just now. During each iteration of the game loop, we need to apply some sort of a gravity to the boxes so that they will fall from the top of the screen.
//Update the position of the rectangles
for (var i=boxArray.length - 1; i >= 0; i--)
if (boxArray[i].y > stage.height)
//Check for collision
boxArray[i].x, boxArray[i].y, BOX_WIDTH, BOX_HEIGHT, mouseX, mouseY))
We start off line 82 by calling a for loop around the boxArray. Notice that we are looping from the last element of the array to the first, i.e. a backwards loop. This is important as we will require to splice away objects from the array whenever a collision appears. To prevent messing up the order of the elements in the array, a backwards loop is necessary here.
Line 84 then applies that gravity to the boxes, which is simply by increasing their y position by 1. Since we set the game loop to run 30 times a second, that translates to an ideal situation where the boxes drop by 30 pixels a second.
Only for those boxes which have not fallen off the game stage will we proceed to draw them out, as shown in line 89, as well as to do the next step of checking if the mouse click happened on them.
//Check for collision
You can see in line 91 that we check first if the mouse click happened by comparing if the variable isClicked is true. Remember from earlier codes that isClicked will only be true if the player had clicked on the game stage.
If so, we then call upon this function hitTestPoint which we will explain in the next step to check if the mouseX and mouseY coordinates, happened on this particular box we are checking now. Naturally, if that is the case, we know that a collision has occured, and we increase the score of the player, as well as to remove away that box.
Step 3 - hitTestPoint For Collision Detection
Those of you familiar with Flash Actionscript may remember that hitTestPoint is a function defined automatically for you. Since we are coding everything from scratch here without access to more complicated libraries, we will have to define this function ourselves.
But fret not. =)
function hitTestPoint(x1, y1, w1, h1, x2, y2)
//x1, y1 = x and y coordinates of object 1
//w1, h1 = width and height of object 1
//x2, y2 = x and y coordinates of object 2 (usually midpt)
if ((x1 <= x2 && x1+w1 >= x2) &&
(y1 <= y2 && y1+h1 >= y2))
I hope you are not surprised (even if so, I hope it's a pleasant surprise) that the function is so simple. Some comments have been added at the start of the function block to explain what each of the parameters do.
Basically, we are just checking if x2 and y2 (which we passed in as mouseX and mouseY) fall within the 4 corners of the object we're checking collision for (which in our case here, the rectangles). If so, we simple return true, else false.
Some of you may spot immediately that this function has its shortcomings, namely so when the object is not a rectangle shape. However, such cases are usually more of an exception than a norm, and even so, there are always some tricks we can play with to make the entire game play experience smooth and believable.
Until Next Time
You can see how we did collision detection simply by comparing the coordinates of both objects. You should also understand now why many classic games have fairly squarish game sprites. The more odd-shaped your game sprite is, the higher the chances of a collision detection not looking natural. In later tutorials however, I hope to be able to share with you some tricks we can play with to make collision detection work on odd-shaped objects.
You can view the demo of this tutorial here (opens in new window).
You can download the files used in this tutorial here.