One of the key tricks that have been used for at least 30 years by game developers is using sprite sheets to animate characters. This can be done using the HTML5 canvas tag and I’ve talked about it on here before. But there’s a big difference between making it look like a space ship is tiling to the side and making a animated person look like he’s walking across the screen. For the space ship, all we had to do was change the source position of the sprite sheet if a certain key was pressed, but to make a character look like they’re walking, we’ll have to cycle through a series of images. Check out my earlier tutorial on sprite sheets to understand the basics of what we’re doing.
Here’s a demo of what we’ll be making. And here’s the sprite sheet. I didn’t create this, I got it form a demo made by John Graham. Now, if John Graham has already made a demo, then why am I making another one? John’s demo involves making the character walk by clicking buttons, I wanted it to work with the left and right arrow keys. Plus, I wanted to challenge myself to simplify his a bit and I think I pulled that off.
To power this, we’ll be using requestAnimationFrame() which runs at 60 frames a second and we’ll make our guy take about 4 steps a second. The way to make this work, is when the right or left arrow key is pressed, we’ll have a counter variable that is increased by one every frame and when it gets to a multiple of 15, it will change the source position of the sprite sheet. Then when it gets to a certain number, in our case 90 (15 for each frame on the sprite sheet), it will go back to the first frame. Also, it will reset to the standing frame when the key stops being pressed.
Now onto the code. First, here’s all our variables:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| var canvas = document.getElementById('canvas'), // get our canvas tag in the DOM
ctx = canvas.getContext('2d'), // set the context of the canvas
character = new Image(), // create a new image object for our sprite sheet
player = { // create our player object
x: 0, // set the player’s x position
y: 50, // set the player’s y position
w: 104, // set the player’s width
h: 149, // set the player’s height
sx: 0, // set the player’s image’s source x position
sy: 301, // set the player’s image’s source y position
faceRight: true, // this will tell the code our player is facing right to start
faceLeft: false, // this is set to false so our player will face right
counter: 0, // counter we use to know when to change frames
step: 15, // we change frames every 15 frames, so increase or decrease this number if you want the player to walk faster or slower
nextStep: 0, // this increases with each frame change
endStep: 90, // when counter equals this number, everything resets and we go back to the first frame
start: { // sets the start postions of our source
rightX: 0, // start x position when facing right
leftX: 100, // start x position when facing left
y: 301 // start y position is the same for both
}
},
key = { // the variables we’ll use to see if a key is being pressed
right: false,
left: false
}; |
The comments explain what all the variables are for. Next, we’ll take a look at the function that draws our character:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function drawPlayer() {
if (key.right === true) {
move(0, true, false);
player.x += 1;
if (player.x > canvas.width + player.w + 1) {
player.x = -player.w;
}
}
if (key.left === true) {
move(150, false, true);
player.x -= 1;
if (player.x < -player.w - 1) {
player.x = canvas.width + player.w;
}
}
if (key.right === false && player.faceRight === true) {
player.sx = player.start.rightX;
reset();
}
if (key.left === false && player.faceLeft === true) {
player.sx = player.start.leftX;
reset();
}
ctx.drawImage(character, player.sx, player.sy, player.w, player.h, player.x, player.y, player.w, player.h);
} |
The drawPlayer() function’s main purpose is to key if the user is pressing one of the arrow keys and if they are which one. If a key isn’t being pressed, then which direction is the player facing. It then calls the appropriate function and draws the character on the canvas. If the right arrow key is pressed, then a function called move is called, a few things are passed to it which we’ll look at in a minute. Then player.x is increased by one, moving the character across the canvas. Finally, it checks to see if the character has gone off the canvas completely and if it has, the character is repositioned to the other side of the canvas instead of just stopping it at the edge. When the left arrow is pressed, the same thing happens, just in the opposite direction. Finally, when the key is not pressed anymore and depending on which way the character is facing, the source image positioning is reset.
Let’s take a look at the move function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function move(yPos, right, left) {
player.faceRight = right;
player.faceLeft = left;
if (player.counter === player.endStep) {
player.sx = 0;
player.counter = 0;
player.nextStep = player.step;
} else if (player.counter === player.nextStep) {
if (player.sy === player.start.y) {
player.sx = 0;
} else if (player.sy === yPos) {
player.sx += player.w;
}
player.sy = yPos;
player.nextStep += player.step;
}
player.counter += 1;
} |
Remember in the drawPlayer() function that we were passing a few things to the move function? Well, here’s what they meant, the first one was the y position of the source image, the second two say which way the player is facing. We first assign the true or false values to player.faceRight and player.faceLeft. Next, we check to see if player.counter has reached the limit we’ve set for it of 90, which is what player.endStep is equal to. If it has, then we reset the source image x position to 0, player.counter to 0 and player.nextStep back to 15 which is what player.step is set to. This will have mean that we’ve got to the final frame and want to start the walk cycle all over again. Next, if player.counter doesn’t equal player.endStep, then we check to see if it equals player.nextStep, which is our check to see if we need to change to the next character frame on our sprite sheet, and remember, we switch every 15 frames that we draw per second. If player.counter does equal player.nextStep, we need to check one more thing, are we just starting to walk? If we are then we set the player.sx to 0 or else we’d skip to the second frame of our walk cycle. If we haven’t just started walking, then we increase our source position by the width of our the width of our viewing area and we’ll see the next frame in our walk cycle. Finally, we increase player.counter by 1.
Finally, we have our reset function, which just resets everything back to our starting values when the player stops moving:
1 2 3 4 5
| function reset() {
player.sy = player.start.y;
player.counter = 0;
player.nextStep = 0;
} |
You can take a look at the complete code by viewing the source on the demo. Hopefully this helped you out.