LESSON 16 - April 19, 2013

HTML5 GAME DEVELOPMENT

Operation C.A.N.T (Canvas Ain't No Thang) LESSON # 16

Ben W. Savage

Now let's look at some ways to optimize what we started yesterday. Here's the code we began with:

<!doctype html>
<title>BLASTOFF!</title>
<canvas id = "canvas" width = "550" height = "400"></canvas>
<style>
canvas
{
background-color: #dddddd;
}
</style>

<script>

var theCanvas = document.getElementById("canvas");
var context = theCanvas.getContext("2d");

var altitude = 395;

window.setInterval(drawRectangle,24);

function drawRectangle()
{
context.clearRect(0,0,canvas.width,canvas.height);
context.fillStyle = "#44ff11";
context.fillRect(canvas.width/2 - 25,altitude,50,50);
}

window.addEventListener("keydown",onKeyDown,false);

function onKeyDown(event)
{
if (event.keyCode == "38")
{
altitude -= 10;
}
}
if (altitude < 0)
{
altitude = 400;
}
</script>

Now let's go back to the drawing board and figure out a more efficient way to get this square to move all around the canvas.

First off, as I explained yesterday, we need an engine. For engine I mean a constantly-running something-or-other that will update my code continuously. This will be important for the actual movement part. For this we'll use a setInterval timer. That will redraw our rectangle at a constant interval and serve our purpose perfectly.

So what else? What sort of variables will we need?

Well, first of all, we'll need to store the velocity of the rectangle in a variable for both directions. For this we'll use VX and VY. That's much better than those silly altitude/longitude values we attributed before. Then we'll need the rectX and rectY values that we'll plug into our rectangle.

After that we'll need to attach some keyboard event listeners to these guys and inside the functions we'll specify how much we're going to increase or decrease VX and VY every time we hit a particular key. We'll add both a keyup and a keydown listener and our keyup listener will serve as a sort of brake which will put the VX or VY value to 0, stopping our rectangle in its tracks.

So let's skip ahead and look at what our finished file will be:

<!doctype html>
<title>BLASTOFF!</title>
<canvas id = "canvas" width = "550" height = "400"></canvas>
<style>
canvas
{
background-color: #dddddd;
}
</style>

<script>

var theCanvas = document.getElementById("canvas");
var context = theCanvas.getContext("2d");

var rectX = 100;
var rectY = 100;

var VX = 0;
var VY = 0;

window.setInterval(drawRectangle,10);



function drawRectangle()
{
context.clearRect(0,0,canvas.width,canvas.height);
context.fillStyle = "#44ff11";
context.fillRect(rectX,rectY,50,50);

rectX += VX;
rectY += VY;
}

window.addEventListener("keydown",onKeyDown,false);
window.addEventListener("keyup",onKeyUp,false);

function onKeyDown(event)
{
if (event.keyCode == "37")
{
VX = -3;
}
else if (event.keyCode == "39")
{
VX = 3;
}
if (event.keyCode == "38")
{
VY = -3;
}
else if (event.keyCode == "40")
{
VY = 3;
}
}

function onKeyUp(event)
{
if (event.keyCode == "37")
{
VX = 0;
}
else if (event.keyCode == "39")
{
VX = 0;
}
if (event.keyCode == "38")
{
VY = 0;
}
else if (event.keyCode == "40")
{
VY = 0;
}
}

</script>

Say, that's pretty smooth! Silky smooth, I might add! Make sure you set your setInterval to 10 milliseconds. If you're curious, go ahead and try different values. A little higher though, and you'll make your ships' movement super choppy. A little less and your ship will be flying all over the screen recklessly!

Fun, huh? We've finally made it! Our code is actually much simpler too!

Now let's throw in some boundaries. First off, let's make our ship come to a halt when it hits the walls. With this new code, it shouldn't be much of a problem at all!

One thing we should have done last tutorial was save the width and height for our retangle. This will make for much smoother blocking and screen wrapping in our games if we punch this into our code. Sure, we could hard-code them in, but today we're aiming for best practices. Let's learn it the right way!

Go ahead and make those variables right now:

var rectWidth = 50;
var rectHeight = 50;

So now we need to specify what happens when our green rectangle touches the margins of our canvas. Hopefully the margins will hold us in, but it will look a million times neater if we judge our collision with the margins according to the width and height values we just created. Let's add the following to our drawRectangle function. Why there? Because if you remember my long-winded speech from yesterday, we need a place for our code to be run constantly and seeing as we need to check not once, but CONSTANTLY where our ship is and whether or not it's hitting the walls, our if statements will need to reside in the drawRectangle function. Got it?

This is what it will look like:

if (rectX + rectWidth > canvas.width)
{
rectX = canvas.width - rectWidth;
}
else if (rectX < 0)
{
rectX = 0;
}

if (rectY < 0)
{
rectY = 0;
}
else if (rectY + rectHeight > canvas.height)
{
rectY = canvas.height - rectHeight;
}

If ya plug this right into your drawRectangle function, you'll see the rectangle stop perfectly at the edge of the canvas on all four sides. ALWAYS keep in mind that canvas moves stuff in relation to their TOP-LEFT corner! Don't forget that, especially you Flash heads! This is an IMPORTANT difference!

So what's happening in the code? Well, a lot of it should be pretty self-explanatory. When we make the collision on the right, we need to ensure that the contact with the wall is preceded by the entire width of the rectangle, otherwise it won't stop until it reaches the top-left corner. And at that point it'll already be off the screen! Same with the bottom of the screen. Whereas in Flash we're used to doing this by half-widths and half-heights, here it works perfectly without for our left and top margins, BUT we need to add an entire width and height to our right and bottom sides. Make sense?

Onwards to screen wrapping!

This shouldn't be too difficult at all! In fact, I'm not going to say much about it at all! See for yourself! Just remember that this code ALSO goes into the drawRectangle function:


if (rectX > canvas.width)
{
rectX = 0;
}
else if (rectX < 0 - rectWidth)
{
rectX = canvas.width - rectWidth;
}

if (rectY < 0)
{
rectY = canvas.height;
}
else if (rectY - rectHeight > canvas.height)
{
rectY = 0;
}

Make sense?

For extra credit, try to make a list of the different kinds of games you know that use these blocking + screen wrapping techniques. Can you name a few off the top of your head now? I gave the name of one away yesterday if you were paying attention!

So that's a wrap for boundaries for the time being. Thought things were getting interesting in these past few lessons? Wait 'til you see what's in store for us in the next few lessons!

Save this file somewhere, by the way. We'll be using it over and over in our lessons on physics. But don't let the name "physics" fool you: this stuff really won't be that hard.

So stay tuned and I hope you enjoyed! As always, thanks for following along! Code didn't work for you? Hate me? Are you my illegitimate child? Send input anytime!

Until tomorrow!

-Ben
@benwhi
Onward to Lesson Seventeen!
Back to Lesson Fifteen!
Back to Index