This simple hit test exercise combines two previous ones, so I won’t go through the “old” parts of the code. Basically, it adds code to our “Drag square” exercise, mostly from the “Trail” exercise, and also a simple collision test.

This is the code for the exercise:

<!DOCTYPE html> <html> <head> <title>Hit test</title> <script type="text/javascript"> var canvas,ctx; var x, y, size; var mouseX, mouseY; var dragging; //circle variables var xC, yC, radius; var speedX, speedY; var hitCounter; function setup(){ //canvas setup canvas=document.getElementById("myCanvas"); ctx=canvas.getContext("2d"); //note 1-if you want a trail, draw background in setup() //ctx.fillStyle="black"; //ctx.fillRect(0,0,canvas.width,canvas.height); //variables setup x=50; y=50; size=50; dragging = false; //circle variables setup xC= 300; yC = 300; radius = 50; speedX = Math.random()*5+5; speedY = Math.random()*5+5; hitCounter =0; //function calls canvas.addEventListener("mousemove",doMouseMove); canvas.addEventListener("mousedown",doMouseDown); canvas.addEventListener("mouseup",doMouseUp); setInterval(draw,10); setInterval(drawCircle,10); } function draw(){ //note 2-if you don't want a trail, draw background in draw() ctx.fillStyle="black"; ctx.fillRect(0,0,canvas.width,canvas.height); //following is the same as if(dragging==true){ if(dragging){ x=mouseX-size/2; y=mouseY-size/2; } ctx.fillStyle="rgba(255,126,0,0.9)"; ctx.fillRect(x,y,size,size); ctx.strokeStyle="rgba(255,255,255,0.9)"; ctx.strokeRect(x,y,size,size); } function drawCircle(){ ctx.fillStyle="rgb(255,0,0)"; ctx.beginPath(); ctx.arc(xC,yC,10,0,Math.PI*2,false); ctx.closePath(); ctx.fill(); xC+=speedX; yC+=speedY; if(xC<0||xC>canvas.width){ speedX*=-1; } if(yC<0||yC>canvas.height){ speedY*=-1; } var distance = Math.sqrt((x+size/2-xC)*(x+size/2-xC)+(y+size/2-yC)*(y+size/2-yC)); if(distance < radius){ speedX*=-1; speedY*=-1; hitCounter++; console.log("Hits: "+hitCounter); } } function doMouseMove(event){ mouseX = event.pageX-canvas.offsetLeft; mouseY = event.pageY-canvas.offsetTop; //console.log("x:"+mouseX+", y:"+mouseY); } function doMouseDown(){ var right = x+size; var bottom = y+size; if(mouseX>x && mouseX<right && mouseY>y && mouseY<bottom){ dragging=true; console.log("you clicked me!!"); }else{ console.log("missed it, better luck next time"); } } function doMouseUp(){ dragging=false; } </script> </head> <body onload="setup()"> <canvas id="myCanvas" width="400" height="400" style="border:1px solid"> </canvas> </body> </html>

The new bits is the are in the beginning marked as “circle variables”, to be used by the added circle:

//circle variables var xC, yC, radius; var speedX, speedY; var hitCounter;

And also in setup, defining default values and a random value for circle “speed”, between 5 and 10 for both x and y axes:

//circle variables setup xC= 300; yC = 300; radius = 50; speedX = Math.random()*5+5; speedY = Math.random()*5+5; hitCounter =0;

There is a new setInterval:

setInterval(drawCircle,10);

And the corresponding triggered function, drawCircle:

function drawCircle(){ ctx.fillStyle="rgb(255,0,0)"; ctx.beginPath(); ctx.arc(xC,yC,10,0,Math.PI*2,false); ctx.closePath(); ctx.fill(); xC+=speedX; yC+=speedY; if(xC<0||xC>canvas.width){ speedX*=-1; } if(yC<0||yC>canvas.height){ speedY*=-1; } var distance = Math.sqrt((x+size/2-xC)*(x+size/2-xC)+(y+size/2-yC)*(y+size/2-yC)); if(distance < radius){ speedX*=-1; speedY*=-1; hitCounter++; console.log("Hits: "+hitCounter); } }

This function is quite similar to our above mentioned previous exercise, “Trail”, with the exception of the last few lines: the last if clause and the distance variable. So let’s go through that.

We’re using the Pythagorean theorem to check for the distance between circle and square. The theorem states that, in a right triangle “The sum of the areas of the two squares on the legs equals the area of the square on the hypotenuse”. The hypotenuse is, therefore, the square root of the sum of the areas of the two squares on the legs. The two legs, in this case, are the distances between the centers of our two objects: x or y+size/2 for the square, and xC or yC for the circle. So the distance between them can be expressed by the following expression:

Math.sqrt((x+size/2-xC)*(x+size/2-xC)+(y+size/2-yC)*(y+size/2-yC));

If the distance is lower than a certain threshold, let’s say the radius, then we invert the direction of the circle:

if(distance < radius){ speedX*=-1; speedY*=-1;

In the last part of the if clause, our hitCount variable will be incremented, and its output will be shown in the console:

hitCounter++; console.log("Hits: "+hitCounter); }

Of course, this would work better if we had a two circles, and not a circle and a square, but the (somewhat buggy) example still serves to illustrate the concept. Other techniques can be explored for hit tests. For example, following this (non HTML5) tutorial. I found this while looking at this HTML5 asteroid game.

Image from the applet:

Link to the exercise: http://mlab.taik.fi/mediacode/coursefiles/course_2011_10/hit_test.html