JS exercise 14 – Simple hit test

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

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s