One of the best HTML5 features was the introduction of Canvas. Canvas has allowed us to actually create web games, without the need of flash. They run smoothly in nearly every browser that supports them (especially mobile Safari) and they’ve got everything you need to start making a game in.
So today, we’re going to creating the next big hit: Walking Simulator. It’ll be 2d, but also a sidescroller. I’ll have the full demo out on CodePen, which is linked at the end of this post.
To get started, we need to have a position in the DOM for our canvas. So all of our HTML will look like:
<canvas id="canvas"></canvas>
Then to make it take up the entire body, we’ll apply our styles:
html, body, canvas {
width: 100%;
height: 100%;
}
Now that we’re done with that, we can start coding.
var canvas = document.getElementById('canvas'); // get the DOM element
var context = canvas.getContext('2d');
// some browsers will stretch the canvas instead of strictly increasing size. this fixes it
canvas.setAttribute('width', window.innerWidth);
canvas.setAttribute('height', window.innerHeight);
You may notice that we had to specify the ‘2d’ context. That’s because a 2nd (possibly more in the future) context exists, and that’s WebGL (3d). Now, one of the things introduced with Canvas was the requestAnimationFrame
function. This function is special, in that it runs at the refresh rate (although usually 60fps) of the monitor (possibly less if it’s in the background). So we’ll redraw everything inside of it.
requestAnimationFrame(function draw() {
context.clearRect(0, 0, canvas.width, canvas.height); // wipe it clean for redrawing
});
Now for this simulator, we’re going to have a cube that can walk back and forth through different backgrounds. So we’ll need to listen to key events:
var left = false; // when true, the left arrow key is down
var right = false; // when true, the right arrow key is down
// this listens for when left/right are pressed
document.addEventListener('keydown', function (e) {
if (e.keyCode === 37) { // left
left = true;
} else if (e.keyCode === 39) { // right
right = true;
}
});
document.addEventListener('keyup', function (e) {
if (e.keyCode === 37) { // left
left = false;
} else if (e.keyCode === 39) { // right
right = false;
}
});
Now because requestAnimationFrame
can be called 60 times per second or 5 times per second, we’re going to use setInterval
to have a constant rate at which our cube moves:
var x = 0;
setInterval(function moveCube() {
if (left && !right) {
if (x > 0) {
x -= 10;
}
} else if (right && !left) {
// we have 4 backgrounds spanning the width of the window, so keep it inside of that
if (x < window.innerWidth * 3) {
x += 10;
}
}
}, 10);
Now that we’ve finished moving, we need to handle different backgrounds. Because we’re making a sidescroller, we’re going to have to draw the backgrounds. The x position of the backgrounds can be calculated by taking the order that the current background shows in, multiplying it by the width of the window, then subtracting it by the current x
. To make this easier, we’re going to write a function for it:
function drawBackground(color, position) {
context.fillStyle = color; // fillStyle is the current color to draw in
context.fillRect((position * window.innerWidth) - x, 0, window.innerWidth, window.innerHeight);
}
The main part of this function that I just explained is the (position * window.innerWidth) - x
part. This handles most of the positioning for our backgrounds. context.fillRect(x, y, width, height)
then creates a rectangles with the supplied parameters. Our rectangle will have the size and the height of our window. Now let’s go back to our requestAnimationFrame
and start drawing stuff.
requestAnimationFrame(function draw() {
drawBackground('orange', 0);
drawBackground('pink', 1);
drawBackground('aqua', 2);
drawBackground('lightgray', 3);
context.fillStyle = 'black';
var cubeSize = 50; // this will be the size of our cube
context.fillRect((window.innerWidth - cubeSize) / 2, window.innerHeight - cubeSize, cubeSize, cubeSize);
requestAnimationFrame(draw); // keep drawing after this
});
So first off, we have 4 different backgrounds we want to draw. Orange, pink, aqua, and light gray. We supply the position we want them in to our drawBackground(color, position)
function, and it draws those.
Then we want to draw the cube. To make things easier, we defined the size we want the cube to be. Then because it’s a sidescroller, we’re going to center the cube. So we take the width of the window, subtract it by our cube width, and divide that by 2. Then we want our cube on the bottom, so we take the window’s height and subtract the cube size. This works because everything is drawn from the top left corner.
At the end you may notice I added requestAnimationFrame(draw);
. This means that after it’s drawn, it asks requestAnimationFrame
to keep drawing it.
But mobile devices don’t have arrow keys! So we’re going to go ahead and listen to the pointer events. Now since this isn’t supported in all browsers, we’re going to use the handjs polyfill. First off, we’re going to have to detect when the user is pressing down:
var down = false;
document.addEventListener('pointerdown', function(e) {
if (e.clientX < window.innerWidth / 2) {
left = true;
} else if (e.clientX > window.innerWidth / 2) {
right = true;
}
down = true;
});
What this does, is check which side the pointer is on. If it’s on the left half, the we set left to true. If it’s on the right half, we set right to true. Now to make this feel more natural, we’re going to allow the user to move their finger (or mouse) to the other side and have it scroll that way. But since the pointermove
event is called for mice even when when the mouse isn’t down, we need to track when the mouse is down. Once we’re done with that, we can add the movement code:
document.addEventListener('pointermove', function(e) {
if (down) {
if (e.clientX < window.innerWidth / 2) {
left = true;
right = false;
} else if (e.clientX > window.innerWidth / 2) {
right = true;
left = false;
}
}
});
As you can see, the first thing we do is make sure that the mouse pointer (or finger) is down. After that, the code is nearly the same asa the code before, except we set the other direction to false. This is what allows us to move the pointer from side to side. Now all we need to do is listen to pointerup
and cancel everything:
document.addEventListener('pointerup', function(e) {
left = right = down = false;
});
Which that then sets left
, right
, and down
to false.