Canvas Basics – 06 Collision Detection

This is the sixth post in this series looking at the <canvas> element in HTML5.

Each post will give a new element of snippet of code that can be put together to create your own games.

So far we have displayed text on screen, looked at the mouse’s X and Y co-ordinates on the canvas, moved a shape automatically and moved a shape using the keyboard’s arrow keys.

 

This is a follow on from the posts looking at maths in games (click here to read the first post).

If you are not already familiar with HTML, you can read through the posts starting here (click to read).

The first 5 posts in this series can be found at:

  1. Canvas Basics – 01 Hello World
  2. Canvas Basics – 02 Mouse Detection
  3. Canvas Basics – 03 Moving An Object
  4. Canvas Basics – 04 Move An Object Using The Keyboard
  5. Canvas Basics – 05 Limiting Movement To Canvas

You can start reading the above links if you have no HTML/Javascript/Canvas experience, or if you have some of the basics covered already, you can continue with this post.

This post is going to show you how to limit the movement of our object to the canvas area.

 

What you’ll need

For these posts you will need a text editor (Notepad on Windows for example) and a relatively up to date browser (Google Chrome is my preference).

In the screenshots you are about to see, you will see that I am not using Notepad for coding, this is because I am using a different package which allows you to see line numbers, this will make it easier for me to comment on particular lines.

 

Skeleton

If you are following these posts, you should have a skeleton2.html file from the last post.

We are going to make 2 changes to this, if you have not been following these posts, copy the code below to a text editor file and save it as “skeleton2.html”.

 

image of code for skeleton.html file as is

From the last post, change the following 2 pieces of code:

  • LINE 3 – Change the title of your page if you want to, this is not necessary
  • LINE 6 – Change the source of the script to be “game3.js”.  We are going to save our original game1.js file as game2.js and change that.  You will still have game1.js from the last post if you ever wanted to refer back to it.

The final code for skeleton2.html is:

<!DOCTYPE html>
<head>
<title>Collision Detection And Random Positions</title>
</head>
<body>
<canvas id="canvas">
Your browser doesn't support Canvas, try another or update
</canvas>
<script src="game3.js"></script>
</body>
</html>

 

Save this file.

Going forward, you are going to view the “skeleton2.html” file in your browser, but all the code we are about to create will be entered in the game3.js file in your text editor.

 

game3.js

In your text editor, open game2.js and save it as game3.js.

If you have not been following these posts, copy the final code from game2.js below into a blank text editor file and save as game3.js.

 
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var keys = {};
window.addEventListener('keydown', function (e) {
keys[e.keyCode] = true;
e.preventDefault();
});
window.addEventListener('keyup', function (e) {
delete keys[e.keyCode];
});
function Box(options) {
this.x = options.x || 10;
this.y = options.y || 10;
this.width = options.width || 40;
this.height = options.height || 50;
this.color = options.color || '#000000'
this.speed = options.speed || 5;
this.direction = options.direction || 'right';
}
var player = new Box({
X: 10,
y: 10,
width: 50,
height: 50,
color: '#44ee11',
speed: 5
});
function input(player) {
if (37 in keys) {
if (player.x - player.speed > 0) {
player.x -= player.speed;
}
player.direction = 'left';
}
if (39 in keys) {
if (player.x + player.width + player.speed < canvas.width) {
player.x += player.speed;
}
player.direction = 'right';
}
if (38 in keys) {
if (player.y - player.speed > 0) {
player.y -= player.speed;
}
player.direction = 'up';
}
if (40 in keys) {
if (player.y + player.height + player.speed < canvas.height) {
player.y += player.speed;
}
player.direction = 'down';
}
}
function drawBox(box) {
ctx.fillStyle = box.color;
ctx.fillRect(box.x, box.y, box.width, box.height);
}
function update() {
input(player);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBox(player);
}
 
function loop() {
update();
draw();
window.requestAnimationFrame(loop);
}
loop();

 

Make sure that the game3.js and skeleton2.html files are both saved in the same location/folder.

 

 

Canvas Basics – Collision Detection

This post will add a second object to our canvas area.

We will test to see if the player object and the new object have collided.

 

Create A New Object

First we need to add another object onscreen, we will call this object “food”.

This new object will be another type of box, we will give it different values to the player box so it is easier to spot and identify them on screen.

 

image showing code for new food object

 

Just below where we set up the player, we will add another variable of the Box type and call it food.

The code from lines 37 to 43 do this and set the attributes like x and y positions and height and width.

I have entered a different (and randomly selected) color for the food object.

Note there is no speed value for this object because at the minute, I don’t plan on moving it.

This new code is:

var food = new Box({
x: 100,
y: 100,
width: 25,
height: 25,
color: '#ee3344'
});
 

 

We have created a food object, now we need to ‘draw’ it on screen.

image showing updates to draw function

Amend the draw function (starting at line 81) to also draw the box type called food.

function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBox(player);
drawBox(food);
}

 

Save your text file and refresh your browser.

You should see a new object on screen as well as your player object.

If you move the player object to the same place as the food object, nothing happens.

image of 2 objects in same spot no collision detection has happened

The reason nothing has happened is because we didn’t give instructions on what to do if two objects are in the same spot.

We need to detect this (often called collision detection) and then give instructions on what to do when it happens.

 

Collision Detection

The code for collision detection can range from relatively straightforward to more complicated.

It depends on how accurate and realistic you want your game, the shapes being used, etc.

We are looking at boxes at the minute, I’m coding these snippets with old arcade style games in mind, for now I’m happy to use a simpler collision detection.

In later examples I may use a slightly more complicated collision detection where Pythagoras, rotation, etc. are needed, but luckily for now we can keep it a bit simpler.

 

The green box in the image below is our player and the red box is our food.

For demonstration, I will label the 4 edges of the player as “my” and then the edge name, so MT = my top; MR = my right; MB = my bottom; and ML = my left.

The edges of the food object will be prefixed with “other”, so OT = other top; OR = other right; OB = other bottom; and OL = other left.

 

In our code, MB will be compared to OT, MR will be compared to OL, ML will be compared to OR and MT will be compared to OB.

A new variable called “collision” will be created and set to “true” by default.

The value of “collision” will be read and if it is true, then we are going to display “COLLISION DETECTION” on screen, otherwise the screen will update as normal.

IMAGE DEMONSTRATING HOW THIS COLLISION DETECTION WILL WORK

Two changes are needed to our code.

The first will be to our constructor function (function Box (options)).

Below where we set the “this.direction” value (at line 27) we will add more code.

We are going to add a property called “collideWith” and it will compare the object in question (“this”, later it will be set as the player object) to otherobject (this will later be set as the food object).

First we set the edges myleft, otherleft, etc.

Image showing collision detection code in constructor function

 

  • LINE 27 – this.collideWith = function(otherobject){ this starts the collision detection for this object and compares to another object
  • LINE 28 – var myleft = this.x; the variable myleft is going to be the X position (left edge) of this object
  • LINE 29 – var myright = this.x + (this.width); the variable myright will be the X value of the right edge of our object.  From the last post you may remember that the right edge is the X position of the object plus the width of the object
  • LINE 30 – var mytop = this.y; mytop will be the Y value of this object, which is the top edge.
  • LINE 31 – var mybottom = this.y + (this.height); similar to myright, the mybottom value is the Y value (the top) plus the height of the object.
  • LINES 32 – 35 these lines are similar to the last 4, but relate to otherobject rather than this.
  • LINE 36 – var collision = true; this creates the collision variable and sets it to true.  The next lines will change it to be false.
  • LINE 37 – if ((mybottom <= othertop) || this if statement now checks the four edges of this object (the player) against the opposite four edges of the other object (food).  The next 3 lines are part of the if test.
  • LINE 38 – (mytop >= otherbottom) ||
  • LINE 39 – (myright <= otherleft) ||
  • LINE 40 – (myleft >= otherright)) {
  • LINE 41 – collision = false; if any of the above 4 lines are true, then the collision variable will be ‘false’.  If they are all false, the variable will default to true, that is, if the two objects are in the same space then they have collided.
  • LINE 42 – } this ends the if statement
  • LINE 43 – return collision; after the if statement we ask to return the value of the collision variable
  • LINE 44 – }  this ends the collideWith property.

The full Box constructor function is now

function Box(options) {
this.x = options.x || 10;
this.y = options.y || 10;
this.width = options.width || 40;
this.height = options.height || 50;
this.color = options.color || '#000000'
this.speed = options.speed || 5;
this.direction = options.direction || 'right';
this.collideWith = function(otherobject) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobject.x;
var otherright = otherobject.x + (otherobject.width);
var othertop = otherobject.y;
var otherbottom = otherobject.y + (otherobject.height);
var collision = true;
if ((mybottom <= othertop) ||
(mytop >= otherbottom) ||
(myright <= otherleft) ||
(myleft >= otherright)) {
collision = false;
}
return collision;
}
}

 

The second piece of code to change is to the draw function.

Here we are going to make use of the collideWith property.  We will check if the player collides with the food object.

If it does, then we will display “COLLISION DETECTED” on screen, or else we will continue with the draw function as it was before and the canvas will be cleared and the player and draw objects will be drawn again.

image of draw function to include collision detection

 

  • LINE 101 – if (player.collideWith(food)) { this starts the if statement for the player collideWith food.  What comes between the opening { and closing } will be the instructions for when the player and food collide.
  • LINE 102 – ctx.font = “24px Arial”; we are setting the font size to 24 and font type to Arial
  • LINE 103 – ctx.fillStyle = “rgb(255, 0 ,255)”; this is another way of defining color using Red, Green and Blue values.  The values range from 0 to 255, so 255 Red and 255 Blue with no green makes purple.
  • LINE 104 – ctx.fillText(“COLLISION DETECTED”, 50, 50); we will display the text COLLISION DETECTED at position 50 on the X and Y axes.
  • LINE 105 – } else { the first closing } bracket ends the if statement.  If the if statement is not true then we are saying or else follow the instructions contained between the next opening and closing { } brackets.  The code that will be contained between these brackets will be the code that was in our draw function before we made these changes.  A closing } bracket was added to the end of them.

The full draw function is now

function draw() {
if (player.collideWith(food)) {
ctx.font = "24px Arial";
ctx.fillStyle = "rgb(255,0,255)";
ctx.fillText("COLLISION DETECTED", 50, 50);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBox(player);
drawBox(food);
}
}

 

Save your text file and refresh your browser.

image showing collision detection message

Now you should see the message on screen when your player object collides with the food object.

 

Review

At the minute, you may notice that the collision detection message appears on screen but if you keep pressing the arrow keys it eventually goes away and the player is displayed on the other side of the food.

In practical use, when you detect a collision you would normally want to do something more than just display a message on screen.

In the next post, when the player collides with the food we will have the food redrawn somewhere else.

 

For now, our game3.js could should look like this:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var keys = {};
window.addEventListener('keydown', function (e) {
keys[e.keyCode] = true;
e.preventDefault();
});
window.addEventListener('keyup', function (e) {
delete keys[e.keyCode];
});
function Box(options) {
this.x = options.x || 10;
this.y = options.y || 10;
this.width = options.width || 40;
this.height = options.height || 50;
this.color = options.color || '#000000'
this.speed = options.speed || 5;
this.direction = options.direction || 'right';
this.collideWith = function(otherobject) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobject.x;
var otherright = otherobject.x + (otherobject.width);
var othertop = otherobject.y;
var otherbottom = otherobject.y + (otherobject.height);
var collision = true;
if ((mybottom <= othertop) ||
(mytop >= otherbottom) ||
(myright <= otherleft) ||
(myleft >= otherright)) {
collision = false;
}
return collision;
}
}
var player = new Box({
X: 10,
y: 10,
width: 50,
height: 50,
color: '#44ee11',
speed: 5
});
var food = new Box({
x: 100,
y: 100,
width: 25,
height: 25,
color: '#ee3344'
});
function input(player) {
if (37 in keys) {
if (player.x - player.speed > 0) {
player.x -= player.speed;
}
player.direction = 'left';
}
if (39 in keys) {
if (player.x + player.width + player.speed < canvas.width) {
player.x += player.speed;
}
player.direction = 'right';
}
if (38 in keys) {
if (player.y - player.speed > 0) {
player.y -= player.speed;
}
player.direction = 'up';
}
if (40 in keys) {
if (player.y + player.height + player.speed < canvas.height) {
player.y += player.speed;
}
player.direction = 'down';
}
}
function drawBox(box) {
ctx.fillStyle = box.color;
ctx.fillRect(box.x, box.y, box.width, box.height);
}
function update() {
input(player);
}
function draw() {
if (player.collideWith(food)) {
ctx.font = "24px Arial";
ctx.fillStyle = "rgb(255,0,255)";
ctx.fillText("COLLISION DETECTED", 50, 50);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBox(player);
drawBox(food);
}
}
function loop() {
update();
draw();
window.requestAnimationFrame(loop);
}
loop();

Disclaimer

I am not claiming that the code that I use in these examples is the best, most efficient and up to date code.  

Code gets updated and improved over time, what is best practice today may not be tomorrow.

email

Comments are closed

Follow

Get every new post delivered to your Inbox

Join other followers:

%d bloggers like this: