logo
468x60-2-495


  • Home
  • Privacy Policy
  • About
search
top
Oct 30, 2011 Posted on Oct 30, 2011 in Hints and Tips | 10 comments

HTML5 Avoider Game Tutorial: Multiple Moving Enemies

This entry is part 2 of 2 in the series HTML5 Avoider Game Tutorial

In the first part of this series, you learned the basics of using JavaScript and the canvas element to make a very simple HTML5 avoider game. But it’s too simple – the single enemy doesn’t even move – there’s no challenge! In this tutorial, you’ll learn how to create a never-ending stream of enemies, all falling from the top of the screen.


Refresher

In the first part of the tutorial we covered quite a few concepts: drawing images to the screen, interacting between HTML and JavaScript, detecting mouse actions, and the if statement. You can download the source files here if you want to dive in to this part of the tutorial, though I recommend reading all parts in order.

Our game’s HTML page contains a canvas element, which triggers a JavaScript function called drawAvatar() when it is clicked. That function is inside a separate file called main.js, and it does two things:

  • Draws a copy of avatar.png to the canvas.
  • Sets up an event listener to call another function, called redrawAvatar(), whenever the mouse moves over the canvas.

The redrawAvatar() function is also inside main.js; unlike drawAvatar() it accepts a parameter – called mouseEvent – which is automatically passed to it by the event listener. This mouseEvent contains information about the mouse’s position. The function does four things:

  • Clears the canvas.
  • Draws a copy of avatar.png to the canvas, at the mouse’s position.
  • Draws a copy of enemy.png to the canvas, at a specified position.
  • Checks to see whether the avatar and enemy are close enough to each other to be overlapping, and displays an alert() if so.

All clear? If not, try the warm up challenge.


Warm Up Challenge

If it’s been a while since you read the first part of the series (or if you just want to check that you understand what’s going on), have a go at these little exercises. They’re completely optional and separate to the actual tutorial, so I recommend working on a copy of your project rather than the original. You can complete all of these exercises using only information from the first part of the series.

Easy

Remember that drawImage() works like a potato stamp. Use it to create an unbroken ring of enemies around the edge of your canvas, like this:

HTML5 avoider game tutorial

(If you get bored of copying and pasting all those statements, feel free to make it a smaller ring – you could resize the canvas, too, if you like.)

Medium

Make the “you hit the enemy” alert appear whenever the avatar hits the edge of the ring. (To test this, remember that you can hit Enter to dismiss the alert; you don’t have to click OK.)

Hard

That alert will come up when you try to move your mouse from outside the canvas to inside it, which is really annoying if you’ve already clicked the canvas. Make it possible to move into the canvas without triggering the alert – but once inside, make the alert appear whenever the avatar touches the ring of enemies.


Make the Enemy Move

We’re going to make the enemy fall down from the top of the screen. For now, we’ll focus on making it move rather than on detecting a collision, so “comment out” the lines in redrawEnemy() that deal with collisions, like so:

//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
//	alert("You hit the enemy!");
//}

Remember: two forward slashes tell the browser “ignore everything on this line from here on”. Earlier on we used this to create comments – little reminders of what certain bits of code do – but here we’re using it for another purpose: stopping certain bits of code from running without completely deleting them. This makes it easy for us to put the code back in later.

At the moment, the enemy is redrawn, in the same position, whenever we move the mouse:

gameCanvas.getContext("2d").drawImage(enemyImage, 250, 150);

Do you remember the Math.random() function from the first part of the tutorial? It returns a random number between zero and one; multiplying it by 300 (the height of the canvas) would give us a number between 0 and 300. What happens if we draw the enemy at a random y-position between 0 and 300 every time the mouse was moved? Let’s try it out; modify that line like so:

gameCanvas.getContext("2d").drawImage(enemyImage, 250, Math.random() * 300);

Try it out here!

It looks like the enemy is moving randomly up and down a certain line, but only when the mouse is moved. Of course, it’s not actually moving; it’s “teleporting” from one position to the next, but this gives the illusion of movement.

We’d get a better illusion if it only moved in one direction. We can achieve this by making sure the enemy’s y-position only increases, and never decreases.

Consider the following code:

function redrawAvatar(mouseEvent) {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();
	var enemyImage = new Image();
	var enemyY = 0;	

	avatarImage.src = "img/avatar.png";
	enemyImage.src = "img/enemy.png";
	gameCanvas.width = 400;		//this erases the contents of the canvas
	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);

	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);

	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
	//	alert("You hit the enemy!");
	//}
}

See what I’m doing? I set the value of enemyY to 0 at the top of the function, then increased it by one pixel before drawing the enemy. In this way, I’m aiming to make the enemy’s y-position increase by one pixel every time redrawAvatar() is run.

However, there’s a flaw in my logic. The line var enemyY = 0; will reset the enemyY variable to 0 every time redrawAvatar() is run, which means that it’ll always be drawn at a y-position of 1 (because it’ll be increased at line 12).

We need to only set it to 0 once. What if we do that in drawEnemy()? After all, that function is only run once:

function drawAvatar() {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();
	var enemyY = 0;

	avatarImage.src = "img/avatar.png";
	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);

	gameCanvas.addEventListener("mousemove", redrawAvatar);
}

function redrawAvatar(mouseEvent) {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();
	var enemyImage = new Image();

	avatarImage.src = "img/avatar.png";
	enemyImage.src = "img/enemy.png";
	gameCanvas.width = 400;		//this erases the contents of the canvas
	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);

	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);

	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
	//	alert("You hit the enemy!");
	//}
}

Try it out here!

Unfortunately, this doesn’t work at all. The problem lies in a concept called scope. If you use the var keyword to define a variable within a function, then the variable will only be accessible within that function. This means that our redrawAvatar() function cannot access the same enemyY variable that was defined in drawAvatar().

However, if we define a variable outside of all functions, it can be accessed by any one of them! So, try this:

var enemyY = 0;

function drawAvatar() {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();

	avatarImage.src = "img/avatar.png";
	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);

	gameCanvas.addEventListener("mousemove", redrawAvatar);
}

function redrawAvatar(mouseEvent) {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();
	var enemyImage = new Image();

	avatarImage.src = "img/avatar.png";
	enemyImage.src = "img/enemy.png";
	gameCanvas.width = 400;		//this erases the contents of the canvas
	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);

	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);

	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
	//	alert("You hit the enemy!");
	//}
}

Try it out here!

It works – the enemy slides down the screen. However, it only does so while we’re moving the mouse. That’s an interesting game mechanic, but it’s not what I was aiming for.


Make the Enemy Move on Its Own

It’d be much better if the enemy appeared to move of its own accord – meaning, it moves regardless of whether or not the player is moving the mouse. We can do this by triggering its movement (its “teleportations”) based on time rather than on mouse movement.

We can do this by using the setInterval() function. It works like this:

setInterval(functionName, period);

Here, functionName is the name of a function we want to run over and over again, and period is the amount of time (in milliseconds) we want to pass between each call to that function.

Let’s see how this looks:

var enemyY = 0;

function drawAvatar() {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();

	avatarImage.src = "img/avatar.png";
	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);

	gameCanvas.addEventListener("mousemove", redrawAvatar);
	setInterval(redrawEnemy, 1000);
}

function redrawAvatar(mouseEvent) {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();

	avatarImage.src = "img/avatar.png";
	gameCanvas.width = 400;		//this erases the contents of the canvas
	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);

	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
	//	alert("You hit the enemy!");
	//}
}

function redrawEnemy() {
	var enemyImage = new Image();
	enemyImage.src = "img/enemy.png";

	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
}

I’ve moved all the code that deals with moving and drawing the enemy to the new redrawEnemy() function, and I’ve set it to be called every 1,000 milliseconds (every second) using a setInterval() call in drawAvatar(). (Unlike when using an event listener, no parameters automatically get passed to redrawEnemy() when we call it from setInterval().)

Try it out here! Click the canvas, then don’t move your mouse.

HTML5 avoider game tutorial

There are a few things wrong with this:

  • The enemy leaves a trail – this is because the canvas isn’t cleared in redrawEnemy().
  • The enemy moves really slowly – perhaps 1000 milliseconds is too long to wait.
  • When the avatar is moved, the enemy disappears – this is because the enemy is only drawn in redrawEnemy(); in redrawAvatar() the canvas is cleared and the avatar is redrawn, but not the enemy.

Let’s fix these one at a time. First, we’ll clear the canvas in redrawEnemy():

function redrawEnemy() {
	var enemyImage = new Image();
	enemyImage.src = "img/enemy.png";

	gameCanvas.width = 400;		//this erases the contents of the canvas

	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
}

Try it out here!

Hm. Now the avatar disappears whenever the enemy is drawn, and vice-versa. Of course, this makes sense; we clear the canvas in both redrawEnemy() and redrawAvatar(), but never draw both the enemy and the avatar at the same time.

What if we moved the enemy in redrawEnemy() – by increasing the value of enemyY – but actually drew it in redrawAvatar()?

var enemyY = 0;

function drawAvatar() {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();

	avatarImage.src = "img/avatar.png";
	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);

	gameCanvas.addEventListener("mousemove", redrawAvatar);
	setInterval(redrawEnemy, 1000);
}

function redrawAvatar(mouseEvent) {
	var gameCanvas = document.getElementById("gameCanvas");
	var avatarImage = new Image();
	var enemyImage = new Image();
	enemyImage.src = "img/enemy.png";

	avatarImage.src = "img/avatar.png";
	gameCanvas.width = 400;		//this erases the contents of the canvas
	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);	

	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
	//	alert("You hit the enemy!");
	//}
}

function redrawEnemy() {
	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
}

Try it out here!

It sort of works, but we’re back to that problem where the enemy only moves while you’re moving the mouse. However, this time it’s slightly different; to make this more obvious, we can increase the enemy’s speed by reducing the period. Set it to 25 (that’s 1/40th of a second, meaning redrawEnemy() will run 40 times per second):

setInterval(redrawEnemy, 25);

Try it out here!

Compare this with the earlier version where the enemy only moved when the mouse was moving. See the difference? In the new one, the enemy’s position keeps changing, but it does so “behind the scenes”; the actual image of the enemy only moves when the mouse is moved. If you wait a second or so before moving the mouse, the enemy image jumps down the screen to catch up with its actual position.

Separating the enemy’s actual position from the enemy’s image’s position like this is going to let us solve our problem.

Before we move on, are you getting confused by the function names? I am. redrawEnemy() isn’t actually drawing the enemy at all. Let’s rename them to something a bit easier to keep track of.

  • drawAvatar() is run when we start the game, and it sets everything up, so let’s rename it to setUpGame()
  • redrawAvatar() is run whenever the mouse moves, so let’s rename it to handleMouseMovement()
  • redrawEnemy() is run every fraction of a second; it’s as if there’s a clock that ticks 40 times a second, and each tick triggers the function. So, let’s rename it to handleTick()
  • Don’t forget you have to rename all the references to the functions as well, in the event listener and the setInterval(). Here’s what it’ll look like:

    var enemyY = 0;
    
    function setUpGame() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var avatarImage = new Image();
    
    	avatarImage.src = "img/avatar.png";
    	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
    
    	gameCanvas.addEventListener("mousemove", handleMouseMovement);
    	setInterval(handleTick, 25);
    }
    
    function handleMouseMovement(mouseEvent) {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var avatarImage = new Image();
    	var enemyImage = new Image();
    	enemyImage.src = "img/enemy.png";
    
    	avatarImage.src = "img/avatar.png";
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);	
    
    	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
    	//	alert("You hit the enemy!");
    	//}
    }
    
    function handleTick() {
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    }
    

    (You’ll also need to change the HTML page, so that the canvas’s onclick attribute is "setUpGame();" rather than "drawAvatar();".

    I think this makes it easier to see what’s going on:

    • When the mouse moves, we move the avatar’s position, draw the avatar in its current position, and draw the enemy in its current position.
    • When the clock ticks, we move the enemy’s position.
    • We need to draw the enemy and the avatar at the same time (i.e. in the same function).
    • If we only draw the enemy when the mouse moves, then the enemy’s movement is not smooth.

    This makes it easier in turn to see a possible solution:

    • When the mouse moves, move the avatar’s position.
    • When the clock ticks, move the enemy’s position, draw the avatar in its current position, and draw the enemy in its current position.

    Let’s implement that. All we need to do is move the drawing code from handleMouseMovement() to handleTick(), right? Like this:

    var enemyY = 0;
    
    function setUpGame() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var avatarImage = new Image();
    
    	avatarImage.src = "img/avatar.png";
    	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
    
    	gameCanvas.addEventListener("mousemove", handleMouseMovement);
    	setInterval(handleTick, 25);
    }
    
    function handleMouseMovement(mouseEvent) {
    	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
    	//	alert("You hit the enemy!");
    	//}
    }
    
    function handleTick() {
    	var avatarImage = new Image();
    	var enemyImage = new Image();
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    
    	enemyImage.src = "img/enemy.png";
    	avatarImage.src = "img/avatar.png";
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    }
    

    Hmm. That’s not right. We’ve got nothing left in handleMouseMovement(). Ah – but that’s because we haven’t separated the avatar’s image’s position from the avatar’s actual position, like we did with the enemy. So let’s do that:

    var enemyY = 0;
    var avatarX = 0;
    var avatarY = 0;
    
    function setUpGame() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var avatarImage = new Image();
    
    	avatarImage.src = "img/avatar.png";
    	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
    
    	gameCanvas.addEventListener("mousemove", handleMouseMovement);
    	setInterval(handleTick, 25);
    }
    
    function handleMouseMovement(mouseEvent) {
    	avatarX = mouseEvent.offsetX;
    	avatarY = mouseEvent.offsetY;
    
    	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
    	//	alert("You hit the enemy!");
    	//}
    }
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var avatarImage = new Image();
    	var enemyImage = new Image();
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    
    	enemyImage.src = "img/enemy.png";
    	avatarImage.src = "img/avatar.png";
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    }
    

    We have to create new variables to store the avatar’s actual x- and y-positions, and define those variables outside of any function, so that we can access them from anywhere.

    Try it out here!

    This works! (If it’s a little jerky, then try closing some tabs or restarting Chrome; that worked for me.) We now have a moving enemy. Took a while to get there, but the actual code we’ve ended up with isn’t too complex, I hope you’ll agree.

    If you want a challenge, try re-introducing the collision detection alert box. Don’t worry if you have troubles; we’ll go through this again a bit later.

    In the mean time, we’ll look at a problem you probably haven’t come across yet…


    Loading the Images From a Server

    As I mentioned in the first part of this series, if you put your game onto a web server as it is now, it won’t work correctly, even though they work fine when running from your computer. My demos work because I’ve made a slight modification to the code; here’s how the game runs without that code:

    Try it out here.

    What’s going on? Well, it’s to do with the images. Every time the clock ticks, we create a new image and set its source to point to an actual image file. This doesn’t cause problems when the image file is on your hard drive, but when it’s on the Internet, the page might try to download the image before drawing it – leading to the flickering that we can see in the demo.

    Perhaps you can already guess at a solution, based on what we’ve done in this tutorial so far. Just like with the enemy’s and avatar’s positions, we can move the enemy’s and avatar’s images outside of the functions, and re-use them over and over again, without having to define them and set their values in the handleTick() function each time.

    Take a look:

    var enemyY = 0;
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    function setUpGame() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	avatarImage = new Image();
    	enemyImage = new Image();
    	enemyImage.src = "img/enemy.png";
    	avatarImage.src = "img/avatar.png";
    
    	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
    
    	gameCanvas.addEventListener("mousemove", handleMouseMovement);
    	setInterval(handleTick, 25);
    }
    
    function handleMouseMovement(mouseEvent) {
    	avatarX = mouseEvent.offsetX;
    	avatarY = mouseEvent.offsetY;
    
    	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
    	//	alert("You hit the enemy!");
    	//}
    }
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    }
    

    Try it out here – no flickering!

    In case you’re wondering: we could have moved lines 9-12 outside of the functions as well. I chose to put them in setUpGame() simply because they seemed to be more about, well, setting up the game.


    Make Another Enemy

    It’s actually really easy to make another enemy appear on the screen. Remember that images are like potato stamps; that means there’s nothing stopping us from drawing the enemy image onto the canvas in two different places within the same tick:

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 100, enemyY);
    }
    
    HTML5 avoider game tutorial
    Click to try it out.

    Simple!

    You can put them at different heights, like so:

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 100, enemyY - 50);
    }
    
    HTML5 avoider game tutorial
    Click to try it out.

    This is a bit messy, though. Instead, how about just creating an enemyY2 variable?

    var enemyY = 0;
    var enemyY2 = -50;
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    //...
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    	enemyY2 = enemyY2 + 1;
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 100, enemyY2);
    }
    

    Now you can set the initial positions of enemyY and enemyY2 to whatever you want, without having to change the code in handleTick().


    Make Five Enemies

    Try extending what we’ve just done so that there are five enemies, all with different starting points. Take a look at my code if you need to. Here’s a hint: you only need to add code outside of the functions and inside the handleTick() function – no need to touch setUpGame() or handleMouseMovement().

    var enemyY = 0;
    var enemyY2 = -50;
    var enemyY3 = -75;
    var enemyY4 = -120;
    var enemyY5 = -250;
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    //...
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    
    	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
    	enemyY2 = enemyY2 + 1;
    	enemyY3 = enemyY3 + 1;
    	enemyY4 = enemyY4 + 1;
    	enemyY5 = enemyY5 + 1;
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 130, enemyY2);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 300, enemyY3);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 50, enemyY4);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 190, enemyY5);
    }
    
    HTML5 avoider game tutorial
    Click to try it out.

    Make Ten Enemies

    Oh, this is going to get tedious, right? Maintaining all those enemies, and adding three lines of code for each one. Yuck.

    Allow me to introduce arrays. Take a look at this:

    var enemyYPositions = [0, -50, -75, -120, -250];
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    //...
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    
    	enemyYPositions[0] = enemyYPositions[0] + 1;
    	enemyYPositions[1] = enemyYPositions[1] + 1;
    	enemyYPositions[2] = enemyYPositions[2] + 1;
    	enemyYPositions[3] = enemyYPositions[3] + 1;
    	enemyYPositions[4] = enemyYPositions[4] + 1;
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyYPositions[0]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 130, enemyYPositions[1]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 300, enemyYPositions[2]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 50, enemyYPositions[3]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 190, enemyYPositions[4]);
    }
    

    This gives us the exact same result as before, but:

    • All of the enemies’ y positions are defined in a single line, and
    • We get an easy way to refer to any of these positions: enemyYPosition[enemyNumber].

    This type of variable is called an array; it’s a way of holding a list of values (or even of other variables), and lets us retrieve any item from that list using a number. Note that the first element of an array is number 0, the second value is number 1, and so on – we call arrays “zero-based” for this reason.

    Looping

    Now take a look at this section of code:

    	enemyYPositions[0] = enemyYPositions[0] + 1;
    	enemyYPositions[1] = enemyYPositions[1] + 1;
    	enemyYPositions[2] = enemyYPositions[2] + 1;
    	enemyYPositions[3] = enemyYPositions[3] + 1;
    	enemyYPositions[4] = enemyYPositions[4] + 1;
    

    We’re doing the same thing, over and over again, to different items in the array. Each line of code is the same as all of the others, except that the number inside the square brackets changes. This is great, because we can write code to say “do this same thing five times, but changing one number each time”. For example:

    var currentNumber = 0;
    while (currentNumber < 5) {
    	alert(currentNumber);
    	currentNumber = currentNumber + 1;
    }
    

    If you put this in your JS file (in setUpGame(), for example), it would make the page display five alert boxes: the first would say “0″; the second would say “1″; and so on up to “4″. In other words, it’s equivalent to doing this:

    var currentNumber = 0;
    alert(currentNumber);
    currentNumber = currentNumber + 1;
    alert(currentNumber);
    currentNumber = currentNumber + 1;
    alert(currentNumber);
    currentNumber = currentNumber + 1;
    alert(currentNumber);
    currentNumber = currentNumber + 1;
    alert(currentNumber);
    currentNumber = currentNumber + 1;
    

    This is because the while statement acts like a repeated if statement. Remember, if works like this:

    if (condition) {
    	outcome;
    }
    

    “If condition is true, then run outcome.”

    while works like this:

    while (condition) {
    	outcome;
    }
    

    “As long as condition remains true, keep running outcome.”

    It’s a subtle difference, but a really important one. An if block will run just once, if the condition is true; a while block will run over and over again until the condition stops condition stops being true.

    For this reason, the outcome – the code that’s run inside the while block – usually contains some code that will, eventually, cause condition to stop being true; if it didn’t the code would just repeat itself forever. In our alert box example, we increased the value of currentNumber until it “currentNumber < 5" was no longer true (when currentNumber reached 5, it was no longer less than 5, which is why we never see an alert box containing the number 5).

    Running code over and over again like this is called "looping", and the while block is called a "loop". Let's now take this code:

    	enemyYPositions[0] = enemyYPositions[0] + 1;
    	enemyYPositions[1] = enemyYPositions[1] + 1;
    	enemyYPositions[2] = enemyYPositions[2] + 1;
    	enemyYPositions[3] = enemyYPositions[3] + 1;
    	enemyYPositions[4] = enemyYPositions[4] + 1;
    

    ...and put it into a loop:

    	var currentEnemyNumber = 0;
    	while (currentEnemyNumber < 5) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    	}
    

    Great! Or is it?

    Actually, that's not quite right: we're not changing the value of currentEnemyNumber. This means that we'll just increase the value of enemyYPositions[0] over and over again, forever, without ever changing the other enemies' y-positions or ever exiting the loop.

    So, we need to do this:

    	var currentEnemyNumber = 0;
    	while (currentEnemyNumber < 5) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    

    Now it's great.

    Can we apply the same thinking to our other repetitive code? I'm referring to this:

    	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyYPositions[0]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 130, enemyYPositions[1]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 300, enemyYPositions[2]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 50, enemyYPositions[3]);
    	gameCanvas.getContext("2d").drawImage(enemyImage, 190, enemyYPositions[4]);
    

    Unfortunately, something like this won't work:

    	var currentEnemyNumber = 0;
    	while (currentEnemyNumber < 5) {
    		gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyYPositions[currentEnemyNumber]);
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    

    ...because not all enemies have an x-position of 250. But we can make it work, if we move all the enemies x-positions to another array:

    var enemyXPositions = [250, 130, 300, 50, 190];
    var enemyYPositions = [0, -50, -75, -120, -250];
    
    //...
    
    var currentEnemyNumber = 0;
    while (currentEnemyNumber < 5) {
    	gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
    	currentEnemyNumber = currentEnemyNumber + 1;
    }
    

    This has the added benefit of keeping all the enemies' x- and y-positions in one neat location, rather than spread out across several lines.

    Let's look at the code in context:

    var enemyYPositions = [0, -50, -75, -120, -250];
    var enemyXPositions = [250, 130, 300, 50, 190];
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    //...
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    
    	while (currentEnemyNumber < 5) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    
    	currentEnemyNumber = 0;
    	while (currentEnemyNumber < 5) {
    		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    }
    

    Note that, on line 22 above, I've reset currentEnemyNumber to 0; if I didn't, the loop for drawing the enemy images wouldn't run even once, as condition would already be false from the earlier loop that moves the enemies. Also note that, when I do this, the loop that draws the enemies doesn't immediately detect that condition is now true again and start moving all the enemies.

    It all works just as it did before.

    HTML5 avoider game tutorial
    Click to try it out.

    The biggest benefit to this is in how easy it is to add another five enemies. We only need to change four lines of code:

    var enemyYPositions = [0, -50, -75, -120, -250, -280, -305, -330, -340, -400];
    var enemyXPositions = [250, 130, 300, 50, 190, 200, 220, 60, 100, 110];
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    //...
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    
    	while (currentEnemyNumber < 10) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    
    	currentEnemyNumber = 0;
    	while (currentEnemyNumber < 10) {
    		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    }
    
    HTML5 avoider game tutorial
    Click to try it out.

    Simple!


    Make Fifteen Enemies

    Here's another exercise for you: modify the code so that it creates fifteen enemies. Again, you only have to alter four lines of code. Take a look at my code if you're not sure:

    var enemyYPositions = [0, -50, -75, -120, -250, -280, -305, -330, -340, -400, -425, -450, -500, -520, -550];
    var enemyXPositions = [250, 130, 300, 50, 190, 200, 220, 60, 100, 110, 30, 300, 150, 190, 90];
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    //...
    
    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    
    	while (currentEnemyNumber < 15) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    
    	currentEnemyNumber = 0;
    	while (currentEnemyNumber < 15) {
    		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    }
    

    Obviously we could continue on like this. But I'm already finding it irritating to change lines 14 and 23 above, and have forgotten to do so a couple of times.

    Fortunately we can automate this, in a way. The number - 5, 10, 15, or whatever - is equal to the number of items in either the enemyXPositions[] or enemyYPositions[] array. We call this the array's length, and can retrieve it from either array by using the .length property - like so:

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    
    	while (currentEnemyNumber < enemyXPositions.length) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    
    	currentEnemyNumber = 0;
    	while (currentEnemyNumber < enemyXPositions.length) {
    		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    }
    

    Or, to be a bit neater:

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    	var numberOfEnemies = enemyXPositions.length;
    
    	while (currentEnemyNumber < numberOfEnemies) {
    		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    
    	gameCanvas.width = 400;		//this erases the contents of the canvas
    	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
    
    	currentEnemyNumber = 0;
    	while (currentEnemyNumber < numberOfEnemies) {
    		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    }
    

    Now you can make as many enemies as you want, just by adding new numbers to the enemyXPositions[] and enemyYPositions[] arrays.


    Re-Introduce Collision Detection

    Remember how collision detection worked? We've had the code sitting in handleMouseMovement() (though commented out) for a while:

    	if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
    		alert("You hit the enemy!");
    	}
    

    It's basically checking whether two rectangles - one that moves with the cursor, and one that sits still on the canvas - are overlapping. But it's hard to understand from that code, so let's take a fresh look.

    First, let's look at it in terms of horizontal overlap:

    HTML5 avoider game tutorial

    Here, the avatar and the enemy are not overlapping. We have: avatarX < avatarX + 30 < enemyX < enemyX + 30

    HTML5 avoider game tutorial

    Here, the avatar and the enemy are overlapping. We have: avatarX < enemyX < avatarX + 30 < enemyX + 30

    HTML5 avoider game tutorial

    Still overlapping. We have: enemyX < avatarX < enemyX + 30 < avatarX + 30

    HTML5 avoider game tutorial

    No longer overlapping. We have: enemyX < enemyX + 30 < avatarX < avatarX + 30

    Taking all these together, we can see that the enemy and avatar are overlapping horizontally if:

    avatarX < enemyX and enemyX < avatarX + 30

    ...or:

    enemyX < avatarX and avatarX < enemyX + 30

    In other words, for horizontal overlap, this condition must be true:

    (avatarX < enemyX && enemyX < avatarX + 30) || (enemyX < avatarX && avatarX < enemyX + 30)

    (Remember, && means "and"; || means "or".)

    It's not hard to come up with a similar condition for vertical overlap:

    (avatarY < enemyY && enemyY < avatarY + 33) || (enemyY < avatarY && avatarY < enemyY + 30)

    (We use 33 here because the avatar is 33 pixels tall, but only 30 pixels wide.)

    For the enemy and avatar to be overlapping, they must be overlapping both horizontally and vertically - so we use the && operator to combine these two conditions:

    ( (avatarX < enemyX && enemyX < avatarX + 30) || (enemyX < avatarX && avatarX < enemyX + 30) ) && ( (avatarY < enemyY && enemyY < avatarY + 33) || (enemyY < avatarY && avatarY < enemyY + 30) )

    That's a long condition! But it's actually quite simple, now that we've broken it down. Let's put it into our code. First, delete the old collision detection code from handleMouseMovement(); we'll put this in handleTick(), after we've moved and redrawn the enemy and avatar, so that it seems fairer.

    To start with, we'll just check for a collision between the avatar and the first enemy:

    function handleTick() {
    
    	//...
    
    	if ( ( (avatarX < enemyXPositions[0] && enemyXPositions[0] < avatarX + 30) || (enemyXPositions[0] < avatarX && avatarX < enemyXPositions[0] + 30) ) && ( (avatarY < enemyYPositions[0] && enemyYPositions[0] < avatarY + 33) || (enemyYPositions[0] < avatarY && avatarY < enemyYPositions[0] + 30) ) ) {
    		alert("You hit the first enemy!");
    	}
    }
    

    It looks messy (so you should probably add a comment to remind yourself of how it works later), but it's got everything we need. Does it work?

    Try it out here!

    It does work! Now we should make it work for all the enemies - and of course we can use a loop to do that:

    	currentEnemyNumber = 0;
    	while (currentEnemyNumber < numberOfEnemies) {
    		if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) {
    			alert("You hit an enemy!");
    		}
    		currentEnemyNumber = currentEnemyNumber + 1;
    	}
    

    Note that this goes inside handleTick(), at the end, so we have to reset currentEnemyNumber to 0. We also need to change the alert box text, since it might not be the first enemy that causes the alert to appear.

    Try it out here!

    All right, this is really shaping into a game! Okay, sure, the alert box is kind of annoying, but it serves our purposes for now.

    There's one more big addition I'd like us to make in this part...


    Make Infinitely Many Enemies

    We can add more and more enemy positions to our arrays - a hundred, a thousand, whatever - but eventually the supply will run out, and the player will have no more enemies to dodge. We need to be able to create new enemies and add their positions to the arrays while the game is running.

    When we want to change the value of a specific item inside enemyXPositions, it's easy: we just write enemyXPositions[3] = 100, or whatever. But how can we add something to the array? Writing enemyXPositions = [100] (or whatever) will just replace the array with a new one, containing just one item.

    The answer is in the arrays' .push() function; this allows us to add an item to an array without creating a new one. To demonstrate this, let's delete all the items from our arrays, and then use .push() to add new ones:

    var enemyYPositions = [];	//empty square brackets means new empty array
    var enemyXPositions = [];
    var avatarX = 0;
    var avatarY = 0;
    var avatarImage;
    var enemyImage;
    
    function setUpGame() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	avatarImage = new Image();
    	enemyImage = new Image();
    	enemyImage.src = "img/enemy.png";
    	avatarImage.src = "img/avatar.png";
    
    	enemyYPositions.push(0);
    	enemyXPositions.push(250);
    
    	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
    
    	gameCanvas.addEventListener("mousemove", handleMouseMovement);
    	setInterval(handleTick, 25);
    }
    
    HTML5 avoider game tutorial
    Click to try it out.

    It works fine; the enemy still moves and the collision detection still works. It's exactly the same as if we'd started the code with:

    var enemyYPositions = [0];
    var enemyXPositions = [250];
    

    So what happens if we push the new values onto the arrays inside handleTick(), rather than inside setUpGame()?

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    	var numberOfEnemies = enemyXPositions.length;
    
    	enemyYPositions.push(0);
    	enemyXPositions.push(250);
    
    HTML5 avoider game tutorial
    Click to try it out.

    Hm. It's creating new enemies in the same positions at such a fast rate that they're overlapping. (The one at the top of the screen appears to be above all of the others because that's the last one to get drawn.)

    Let's try to fix this by creating the enemies at random starting x-positions. Remember that Math.random() gets us a random number between 0 and 1, so to get a random number between 0 and 400 - the width of the canvas - we can use Math.random() * 400:

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    	var numberOfEnemies = enemyXPositions.length;
    
    	enemyYPositions.push(0);
    	enemyXPositions.push(Math.random() * 400);
    
    HTML5 avoider game tutorial
    Click to try it out.

    Argh!

    All right, so maybe they're still coming in at too fast a rate...

    Less Enemies Per Second, Please

    Right now, the enemies are being created at a rate of one new enemy per tick - and since a tick is 25ms, there are 40 ticks per second, and therefore 40 new enemies per second.

    Let's reduce this to something more manageable: about two enemies per second.

    Since that's 1/20th of our current rate, we could achieve this by keeping track of how many ticks have passed, and creating a new one on tick number 20, tick number 40, tick number 60, and so on. But I think it'll be more fun if instead we make there be a 1/20 chance of a new enemy being creates on any given tick. This way, sometimes we'd create more than two new enemies in one second, and sometimes we'd create less, but it'd average out to two per second. The uncertainty involved would make the game a little more exciting (though perhaps "exciting" is a poor choice of words at this early stage of the game's development...).

    How can we do this, then? Well, we know that Math.random() create a random number between 0 and 1. Since these random numbers are evenly spread out between 0 and 1, that means there's a 1/20 change of the number generated being somewhere between 0 and... well, 1/20.

    In other words, the chance of Math.random() < 1/20 being true is 1/20.

    So, let's change our code to make use of this fact:

    function handleTick() {
    	var gameCanvas = document.getElementById("gameCanvas");
    	var currentEnemyNumber = 0;
    	var numberOfEnemies = enemyXPositions.length;
    
    	if (Math.random() < 1/20)
    	{
    		enemyYPositions.push(0);
    		enemyXPositions.push(Math.random() * 400);
    	}
    
    HTML5 avoider game tutorial
    Click to try it out.

    Much better! Feel free to experiment with that condition to find a value that works for you.


    Wrapping Up

    That's it for this part of the series. We've now built a rudimentary game - not a polished game, not a particularly fun game, but a game nonetheless.

    If you'd like to challenge yourself before the next part, have a go at making these changes:

    • Easy: The enemies 'pop' onto the top of the canvas; make them slide in, instead.
    • Easy: Some of the enemies are created partially off the side of the canvas; stop this happening.
    • Medium: The enemies all move at exactly the same speed, which is pretty dull; allow them to have different speeds.
    • Hard: The avatar can move off the right edge of the canvas, making it impossible for any enemies to touch it (assuming you've completed the second easy challenge). Make sure the avatar stays inside the boundaries.

    Enjoy!



    View full post on Activetuts+

    banner ad

    10 Responses to “HTML5 Avoider Game Tutorial: Multiple Moving Enemies”

    1. Michael James Williams says:
      October 30, 2011 at 1:11 am
      This entry is part 2 of 2 in the series HTML5 Avoider Game Tutorial

      In the first part of this series, you learned the basics of using JavaScript and the canvas element to make a very simple HTML5 avoider game. But it’s too simple – the single enemy doesn’t even move – there’s no challenge! In this tutorial, you’ll learn how to create a never-ending stream of enemies, all falling from the top of the screen.


      Refresher

      In the first part of the tutorial we covered quite a few concepts: drawing images to the screen, interacting between HTML and JavaScript, detecting mouse actions, and the if statement. You can download the source files here if you want to dive in to this part of the tutorial, though I recommend reading all parts in order.

      Our game’s HTML page contains a canvas element, which triggers a JavaScript function called drawAvatar() when it is clicked. That function is inside a separate file called main.js, and it does two things:

      • Draws a copy of avatar.png to the canvas.
      • Sets up an event listener to call another function, called redrawAvatar(), whenever the mouse moves over the canvas.

      The redrawAvatar() function is also inside main.js; unlike drawAvatar() it accepts a parameter – called mouseEvent – which is automatically passed to it by the event listener. This mouseEvent contains information about the mouse’s position. The function does four things:

      • Clears the canvas.
      • Draws a copy of avatar.png to the canvas, at the mouse’s position.
      • Draws a copy of enemy.png to the canvas, at a specified position.
      • Checks to see whether the avatar and enemy are close enough to each other to be overlapping, and displays an alert() if so.

      All clear? If not, try the warm up challenge.


      Warm Up Challenge

      If it’s been a while since you read the first part of the series (or if you just want to check that you understand what’s going on), have a go at these little exercises. They’re completely optional and separate to the actual tutorial, so I recommend working on a copy of your project rather than the original. You can complete all of these exercises using only information from the first part of the series.

      Easy

      Remember that drawImage() works like a potato stamp. Use it to create an unbroken ring of enemies around the edge of your canvas, like this:

      HTML5 avoider game tutorial

      (If you get bored of copying and pasting all those statements, feel free to make it a smaller ring – you could resize the canvas, too, if you like.)

      Medium

      Make the “you hit the enemy” alert appear whenever the avatar hits the edge of the ring. (To test this, remember that you can hit Enter to dismiss the alert; you don’t have to click OK.)

      Hard

      That alert will come up when you try to move your mouse from outside the canvas to inside it, which is really annoying if you’ve already clicked the canvas. Make it possible to move into the canvas without triggering the alert – but once inside, make the alert appear whenever the avatar touches the ring of enemies.


      Make the Enemy Move

      We’re going to make the enemy fall down from the top of the screen. For now, we’ll focus on making it move rather than on detecting a collision, so “comment out” the lines in redrawEnemy() that deal with collisions, like so:

      
      
      //if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      //	alert("You hit the enemy!");
      //}
      

      Remember: two forward slashes tell the browser “ignore everything on this line from here on”. Earlier on we used this to create comments – little reminders of what certain bits of code do – but here we’re using it for another purpose: stopping certain bits of code from running without completely deleting them. This makes it easy for us to put the code back in later.

      At the moment, the enemy is redrawn, in the same position, whenever we move the mouse:

      
      
      gameCanvas.getContext("2d").drawImage(enemyImage, 250, 150);
      

      Do you remember the Math.random() function from the first part of the tutorial? It returns a random number between zero and one; multiplying it by 300 (the height of the canvas) would give us a number between 0 and 300. What happens if we draw the enemy at a random y-position between 0 and 300 every time the mouse was moved? Let’s try it out; modify that line like so:

      
      
      gameCanvas.getContext("2d").drawImage(enemyImage, 250, Math.random() * 300);
      

      Try it out here!

      It looks like the enemy is moving randomly up and down a certain line, but only when the mouse is moved. Of course, it’s not actually moving; it’s “teleporting” from one position to the next, but this gives the illusion of movement.

      We’d get a better illusion if it only moved in one direction. We can achieve this by making sure the enemy’s y-position only increases, and never decreases.

      Consider the following code:

      
      
      function redrawAvatar(mouseEvent) {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      	var enemyImage = new Image();
      	var enemyY = 0;	
      
      	avatarImage.src = "img/avatar.png";
      	enemyImage.src = "img/enemy.png";
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      

      See what I’m doing? I set the value of enemyY to 0 at the top of the function, then increased it by one pixel before drawing the enemy. In this way, I’m aiming to make the enemy’s y-position increase by one pixel every time redrawAvatar() is run.

      However, there’s a flaw in my logic. The line var enemyY = 0; will reset the enemyY variable to 0 every time redrawAvatar() is run, which means that it’ll always be drawn at a y-position of 1 (because it’ll be increased at line 12).

      We need to only set it to 0 once. What if we do that in drawEnemy()? After all, that function is only run once:

      
      
      function drawAvatar() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      	var enemyY = 0;
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", redrawAvatar);
      }
      
      function redrawAvatar(mouseEvent) {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      	var enemyImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	enemyImage.src = "img/enemy.png";
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      

      Try it out here!

      Unfortunately, this doesn’t work at all. The problem lies in a concept called scope. If you use the var keyword to define a variable within a function, then the variable will only be accessible within that function. This means that our redrawAvatar() function cannot access the same enemyY variable that was defined in drawAvatar().

      However, if we define a variable outside of all functions, it can be accessed by any one of them! So, try this:

      
      
      var enemyY = 0;
      
      function drawAvatar() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", redrawAvatar);
      }
      
      function redrawAvatar(mouseEvent) {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      	var enemyImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	enemyImage.src = "img/enemy.png";
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      

      Try it out here!

      It works – the enemy slides down the screen. However, it only does so while we’re moving the mouse. That’s an interesting game mechanic, but it’s not what I was aiming for.


      Make the Enemy Move on Its Own

      It’d be much better if the enemy appeared to move of its own accord – meaning, it moves regardless of whether or not the player is moving the mouse. We can do this by triggering its movement (its “teleportations”) based on time rather than on mouse movement.

      We can do this by using the setInterval() function. It works like this:

      
      
      setInterval(functionName, period);
      

      Here, functionName is the name of a function we want to run over and over again, and period is the amount of time (in milliseconds) we want to pass between each call to that function.

      Let’s see how this looks:

      
      
      var enemyY = 0;
      
      function drawAvatar() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", redrawAvatar);
      	setInterval(redrawEnemy, 1000);
      }
      
      function redrawAvatar(mouseEvent) {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      
      function redrawEnemy() {
      	var enemyImage = new Image();
      	enemyImage.src = "img/enemy.png";
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      }
      

      I’ve moved all the code that deals with moving and drawing the enemy to the new redrawEnemy() function, and I’ve set it to be called every 1,000 milliseconds (every second) using a setInterval() call in drawAvatar(). (Unlike when using an event listener, no parameters automatically get passed to redrawEnemy() when we call it from setInterval().)

      Try it out here! Click the canvas, then don’t move your mouse.

      HTML5 avoider game tutorial

      There are a few things wrong with this:

      • The enemy leaves a trail – this is because the canvas isn’t cleared in redrawEnemy().
      • The enemy moves really slowly – perhaps 1000 milliseconds is too long to wait.
      • When the avatar is moved, the enemy disappears – this is because the enemy is only drawn in redrawEnemy(); in redrawAvatar() the canvas is cleared and the avatar is redrawn, but not the enemy.

      Let’s fix these one at a time. First, we’ll clear the canvas in redrawEnemy():

      
      
      function redrawEnemy() {
      	var enemyImage = new Image();
      	enemyImage.src = "img/enemy.png";
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      }
      

      Try it out here!

      Hm. Now the avatar disappears whenever the enemy is drawn, and vice-versa. Of course, this makes sense; we clear the canvas in both redrawEnemy() and redrawAvatar(), but never draw both the enemy and the avatar at the same time.

      What if we moved the enemy in redrawEnemy() – by increasing the value of enemyY – but actually drew it in redrawAvatar()?

      
      
      var enemyY = 0;
      
      function drawAvatar() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", redrawAvatar);
      	setInterval(redrawEnemy, 1000);
      }
      
      function redrawAvatar(mouseEvent) {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      	var enemyImage = new Image();
      	enemyImage.src = "img/enemy.png";
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);	
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      
      function redrawEnemy() {
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      }
      

      Try it out here!

      It sort of works, but we’re back to that problem where the enemy only moves while you’re moving the mouse. However, this time it’s slightly different; to make this more obvious, we can increase the enemy’s speed by reducing the period. Set it to 25 (that’s 1/40th of a second, meaning redrawEnemy() will run 40 times per second):

      
      
      setInterval(redrawEnemy, 25);
      

      Try it out here!

      Compare this with the earlier version where the enemy only moved when the mouse was moving. See the difference? In the new one, the enemy’s position keeps changing, but it does so “behind the scenes”; the actual image of the enemy only moves when the mouse is moved. If you wait a second or so before moving the mouse, the enemy image jumps down the screen to catch up with its actual position.

      Separating the enemy’s actual position from the enemy’s image’s position like this is going to let us solve our problem.

      Before we move on, are you getting confused by the function names? I am. redrawEnemy() isn’t actually drawing the enemy at all. Let’s rename them to something a bit easier to keep track of.

      • drawAvatar() is run when we start the game, and it sets everything up, so let’s rename it to setUpGame()
      • redrawAvatar() is run whenever the mouse moves, so let’s rename it to handleMouseMovement()
      • redrawEnemy() is run every fraction of a second; it’s as if there’s a clock that ticks 40 times a second, and each tick triggers the function. So, let’s rename it to handleTick()
      • Don’t forget you have to rename all the references to the functions as well, in the event listener and the setInterval(). Here’s what it’ll look like:

        
        
        var enemyY = 0;
        
        function setUpGame() {
        	var gameCanvas = document.getElementById("gameCanvas");
        	var avatarImage = new Image();
        
        	avatarImage.src = "img/avatar.png";
        	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
        
        	gameCanvas.addEventListener("mousemove", handleMouseMovement);
        	setInterval(handleTick, 25);
        }
        
        function handleMouseMovement(mouseEvent) {
        	var gameCanvas = document.getElementById("gameCanvas");
        	var avatarImage = new Image();
        	var enemyImage = new Image();
        	enemyImage.src = "img/enemy.png";
        
        	avatarImage.src = "img/avatar.png";
        	gameCanvas.width = 400;		//this erases the contents of the canvas
        	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
        	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);	
        
        	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
        	//	alert("You hit the enemy!");
        	//}
        }
        
        function handleTick() {
        	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
        }
        

        (You’ll also need to change the HTML page, so that the canvas’s onclick attribute is "setUpGame();" rather than "drawAvatar();".

        I think this makes it easier to see what’s going on:

      • When the mouse moves, we move the avatar’s position, draw the avatar in its current position, and draw the enemy in its current position.
      • When the clock ticks, we move the enemy’s position.
      • We need to draw the enemy and the avatar at the same time (i.e. in the same function).
      • If we only draw the enemy when the mouse moves, then the enemy’s movement is not smooth.

      This makes it easier in turn to see a possible solution:

      • When the mouse moves, move the avatar’s position.
      • When the clock ticks, move the enemy’s position, draw the avatar in its current position, and draw the enemy in its current position.

      Let’s implement that. All we need to do is move the drawing code from handleMouseMovement() to handleTick(), right? Like this:

      
      
      var enemyY = 0;
      
      function setUpGame() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", handleMouseMovement);
      	setInterval(handleTick, 25);
      }
      
      function handleMouseMovement(mouseEvent) {
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      
      function handleTick() {
      	var avatarImage = new Image();
      	var enemyImage = new Image();
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      
      	enemyImage.src = "img/enemy.png";
      	avatarImage.src = "img/avatar.png";
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, mouseEvent.offsetX, mouseEvent.offsetY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      }
      

      Hmm. That’s not right. We’ve got nothing left in handleMouseMovement(). Ah – but that’s because we haven’t separated the avatar’s image’s position from the avatar’s actual position, like we did with the enemy. So let’s do that:

      
      
      var enemyY = 0;
      var avatarX = 0;
      var avatarY = 0;
      
      function setUpGame() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      
      	avatarImage.src = "img/avatar.png";
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", handleMouseMovement);
      	setInterval(handleTick, 25);
      }
      
      function handleMouseMovement(mouseEvent) {
      	avatarX = mouseEvent.offsetX;
      	avatarY = mouseEvent.offsetY;
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var avatarImage = new Image();
      	var enemyImage = new Image();
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      
      	enemyImage.src = "img/enemy.png";
      	avatarImage.src = "img/avatar.png";
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      }
      

      We have to create new variables to store the avatar’s actual x- and y-positions, and define those variables outside of any function, so that we can access them from anywhere.

      Try it out here!

      This works! (If it’s a little jerky, then try closing some tabs or restarting Chrome; that worked for me.) We now have a moving enemy. Took a while to get there, but the actual code we’ve ended up with isn’t too complex, I hope you’ll agree.

      If you want a challenge, try re-introducing the collision detection alert box. Don’t worry if you have troubles; we’ll go through this again a bit later.

      In the mean time, we’ll look at a problem you probably haven’t come across yet…


      Loading the Images From a Server

      As I mentioned in the first part of this series, if you put your game onto a web server as it is now, it won’t work correctly, even though they work fine when running from your computer. My demos work because I’ve made a slight modification to the code; here’s how the game runs without that code:

      Try it out here.

      What’s going on? Well, it’s to do with the images. Every time the clock ticks, we create a new image and set its source to point to an actual image file. This doesn’t cause problems when the image file is on your hard drive, but when it’s on the Internet, the page might try to download the image before drawing it – leading to the flickering that we can see in the demo.

      Perhaps you can already guess at a solution, based on what we’ve done in this tutorial so far. Just like with the enemy’s and avatar’s positions, we can move the enemy’s and avatar’s images outside of the functions, and re-use them over and over again, without having to define them and set their values in the handleTick() function each time.

      Take a look:

      
      
      var enemyY = 0;
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      function setUpGame() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	avatarImage = new Image();
      	enemyImage = new Image();
      	enemyImage.src = "img/enemy.png";
      	avatarImage.src = "img/avatar.png";
      
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", handleMouseMovement);
      	setInterval(handleTick, 25);
      }
      
      function handleMouseMovement(mouseEvent) {
      	avatarX = mouseEvent.offsetX;
      	avatarY = mouseEvent.offsetY;
      
      	//if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      	//	alert("You hit the enemy!");
      	//}
      }
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      }
      

      Try it out here – no flickering!

      In case you’re wondering: we could have moved lines 9-12 outside of the functions as well. I chose to put them in setUpGame() simply because they seemed to be more about, well, setting up the game.


      Make Another Enemy

      It’s actually really easy to make another enemy appear on the screen. Remember that images are like potato stamps; that means there’s nothing stopping us from drawing the enemy image onto the canvas in two different places within the same tick:

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 100, enemyY);
      }
      
      HTML5 avoider game tutorial
      Click to try it out.

      Simple!

      You can put them at different heights, like so:

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 100, enemyY - 50);
      }
      
      HTML5 avoider game tutorial
      Click to try it out.

      This is a bit messy, though. Instead, how about just creating an enemyY2 variable?

      
      
      var enemyY = 0;
      var enemyY2 = -50;
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      //...
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	enemyY2 = enemyY2 + 1;
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 100, enemyY2);
      }
      

      Now you can set the initial positions of enemyY and enemyY2 to whatever you want, without having to change the code in handleTick().


      Make Five Enemies

      Try extending what we’ve just done so that there are five enemies, all with different starting points. Take a look at my code if you need to. Here’s a hint: you only need to add code outside of the functions and inside the handleTick() function – no need to touch setUpGame() or handleMouseMovement().

      
      
      var enemyY = 0;
      var enemyY2 = -50;
      var enemyY3 = -75;
      var enemyY4 = -120;
      var enemyY5 = -250;
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      //...
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      
      	enemyY = enemyY + 1;    //increase enemyY variable by one pixel. If enemyY is 10, then enemyY + 1 is 11, etc.
      	enemyY2 = enemyY2 + 1;
      	enemyY3 = enemyY3 + 1;
      	enemyY4 = enemyY4 + 1;
      	enemyY5 = enemyY5 + 1;
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 130, enemyY2);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 300, enemyY3);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 50, enemyY4);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 190, enemyY5);
      }
      
      HTML5 avoider game tutorial
      Click to try it out.

      Make Ten Enemies

      Oh, this is going to get tedious, right? Maintaining all those enemies, and adding three lines of code for each one. Yuck.

      Allow me to introduce arrays. Take a look at this:

      
      
      var enemyYPositions = [0, -50, -75, -120, -250];
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      //...
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      
      	enemyYPositions[0] = enemyYPositions[0] + 1;
      	enemyYPositions[1] = enemyYPositions[1] + 1;
      	enemyYPositions[2] = enemyYPositions[2] + 1;
      	enemyYPositions[3] = enemyYPositions[3] + 1;
      	enemyYPositions[4] = enemyYPositions[4] + 1;
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyYPositions[0]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 130, enemyYPositions[1]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 300, enemyYPositions[2]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 50, enemyYPositions[3]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 190, enemyYPositions[4]);
      }
      

      This gives us the exact same result as before, but:

      • All of the enemies’ y positions are defined in a single line, and
      • We get an easy way to refer to any of these positions: enemyYPosition[enemyNumber].

      This type of variable is called an array; it’s a way of holding a list of values (or even of other variables), and lets us retrieve any item from that list using a number. Note that the first element of an array is number 0, the second value is number 1, and so on – we call arrays “zero-based” for this reason.

      Looping

      Now take a look at this section of code:

      
      
      	enemyYPositions[0] = enemyYPositions[0] + 1;
      	enemyYPositions[1] = enemyYPositions[1] + 1;
      	enemyYPositions[2] = enemyYPositions[2] + 1;
      	enemyYPositions[3] = enemyYPositions[3] + 1;
      	enemyYPositions[4] = enemyYPositions[4] + 1;
      

      We’re doing the same thing, over and over again, to different items in the array. Each line of code is the same as all of the others, except that the number inside the square brackets changes. This is great, because we can write code to say “do this same thing five times, but changing one number each time”. For example:

      
      
      var currentNumber = 0;
      while (currentNumber < 5) {
      	alert(currentNumber);
      	currentNumber = currentNumber + 1;
      }
      

      If you put this in your JS file (in setUpGame(), for example), it would make the page display five alert boxes: the first would say “0″; the second would say “1″; and so on up to “4″. In other words, it’s equivalent to doing this:

      
      
      var currentNumber = 0;
      alert(currentNumber);
      currentNumber = currentNumber + 1;
      alert(currentNumber);
      currentNumber = currentNumber + 1;
      alert(currentNumber);
      currentNumber = currentNumber + 1;
      alert(currentNumber);
      currentNumber = currentNumber + 1;
      alert(currentNumber);
      currentNumber = currentNumber + 1;
      

      This is because the while statement acts like a repeated if statement. Remember, if works like this:

      
      
      if (condition) {
      	outcome;
      }
      

      “If condition is true, then run outcome.”

      while works like this:

      
      
      while (condition) {
      	outcome;
      }
      

      “As long as condition remains true, keep running outcome.”

      It’s a subtle difference, but a really important one. An if block will run just once, if the condition is true; a while block will run over and over again until the condition stops condition stops being true.

      For this reason, the outcome – the code that’s run inside the while block – usually contains some code that will, eventually, cause condition to stop being true; if it didn’t the code would just repeat itself forever. In our alert box example, we increased the value of currentNumber until it “currentNumber < 5" was no longer true (when currentNumber reached 5, it was no longer less than 5, which is why we never see an alert box containing the number 5).

      Running code over and over again like this is called "looping", and the while block is called a "loop". Let's now take this code:

      
      
      	enemyYPositions[0] = enemyYPositions[0] + 1;
      	enemyYPositions[1] = enemyYPositions[1] + 1;
      	enemyYPositions[2] = enemyYPositions[2] + 1;
      	enemyYPositions[3] = enemyYPositions[3] + 1;
      	enemyYPositions[4] = enemyYPositions[4] + 1;
      

      ...and put it into a loop:

      
      
      	var currentEnemyNumber = 0;
      	while (currentEnemyNumber < 5) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      	}
      

      Great! Or is it?

      Actually, that's not quite right: we're not changing the value of currentEnemyNumber. This means that we'll just increase the value of enemyYPositions[0] over and over again, forever, without ever changing the other enemies' y-positions or ever exiting the loop.

      So, we need to do this:

      
      
      	var currentEnemyNumber = 0;
      	while (currentEnemyNumber < 5) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      

      Now it's great.

      Can we apply the same thinking to our other repetitive code? I'm referring to this:

      
      
      	gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyYPositions[0]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 130, enemyYPositions[1]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 300, enemyYPositions[2]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 50, enemyYPositions[3]);
      	gameCanvas.getContext("2d").drawImage(enemyImage, 190, enemyYPositions[4]);
      

      Unfortunately, something like this won't work:

      
      
      	var currentEnemyNumber = 0;
      	while (currentEnemyNumber < 5) {
      		gameCanvas.getContext("2d").drawImage(enemyImage, 250, enemyYPositions[currentEnemyNumber]);
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      

      ...because not all enemies have an x-position of 250. But we can make it work, if we move all the enemies x-positions to another array:

      
      
      var enemyXPositions = [250, 130, 300, 50, 190];
      var enemyYPositions = [0, -50, -75, -120, -250];
      
      //...
      
      var currentEnemyNumber = 0;
      while (currentEnemyNumber < 5) {
      	gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
      	currentEnemyNumber = currentEnemyNumber + 1;
      }
      

      This has the added benefit of keeping all the enemies' x- and y-positions in one neat location, rather than spread out across several lines.

      Let's look at the code in context:

      
      
      var enemyYPositions = [0, -50, -75, -120, -250];
      var enemyXPositions = [250, 130, 300, 50, 190];
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      //...
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      
      	while (currentEnemyNumber < 5) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      
      	currentEnemyNumber = 0;
      	while (currentEnemyNumber < 5) {
      		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      }
      

      Note that, on line 22 above, I've reset currentEnemyNumber to 0; if I didn't, the loop for drawing the enemy images wouldn't run even once, as condition would already be false from the earlier loop that moves the enemies. Also note that, when I do this, the loop that draws the enemies doesn't immediately detect that condition is now true again and start moving all the enemies.

      It all works just as it did before.

      HTML5 avoider game tutorial
      Click to try it out.

      The biggest benefit to this is in how easy it is to add another five enemies. We only need to change four lines of code:

      
      
      var enemyYPositions = [0, -50, -75, -120, -250, -280, -305, -330, -340, -400];
      var enemyXPositions = [250, 130, 300, 50, 190, 200, 220, 60, 100, 110];
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      //...
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      
      	while (currentEnemyNumber < 10) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      
      	currentEnemyNumber = 0;
      	while (currentEnemyNumber < 10) {
      		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      }
      
      HTML5 avoider game tutorial
      Click to try it out.

      Simple!


      Make Fifteen Enemies

      Here's another exercise for you: modify the code so that it creates fifteen enemies. Again, you only have to alter four lines of code. Take a look at my code if you're not sure:

      
      
      var enemyYPositions = [0, -50, -75, -120, -250, -280, -305, -330, -340, -400, -425, -450, -500, -520, -550];
      var enemyXPositions = [250, 130, 300, 50, 190, 200, 220, 60, 100, 110, 30, 300, 150, 190, 90];
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      //...
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      
      	while (currentEnemyNumber < 15) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      
      	currentEnemyNumber = 0;
      	while (currentEnemyNumber < 15) {
      		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      }
      

      Obviously we could continue on like this. But I'm already finding it irritating to change lines 14 and 23 above, and have forgotten to do so a couple of times.

      Fortunately we can automate this, in a way. The number - 5, 10, 15, or whatever - is equal to the number of items in either the enemyXPositions[] or enemyYPositions[] array. We call this the array's length, and can retrieve it from either array by using the .length property - like so:

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      
      	while (currentEnemyNumber < enemyXPositions.length) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      
      	currentEnemyNumber = 0;
      	while (currentEnemyNumber < enemyXPositions.length) {
      		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      }
      

      Or, to be a bit neater:

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      	var numberOfEnemies = enemyXPositions.length;
      
      	while (currentEnemyNumber < numberOfEnemies) {
      		enemyYPositions[currentEnemyNumber] = enemyYPositions[currentEnemyNumber] + 1;
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      
      	gameCanvas.width = 400;		//this erases the contents of the canvas
      	gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY);
      
      	currentEnemyNumber = 0;
      	while (currentEnemyNumber < numberOfEnemies) {
      		gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]);
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      }
      

      Now you can make as many enemies as you want, just by adding new numbers to the enemyXPositions[] and enemyYPositions[] arrays.


      Re-Introduce Collision Detection

      Remember how collision detection worked? We've had the code sitting in handleMouseMovement() (though commented out) for a while:

      
      
      	if (mouseEvent.offsetX > 220 && mouseEvent.offsetX < 280 && mouseEvent.offsetY > 117 && mouseEvent.offsetY < 180) {
      		alert("You hit the enemy!");
      	}
      

      It's basically checking whether two rectangles - one that moves with the cursor, and one that sits still on the canvas - are overlapping. But it's hard to understand from that code, so let's take a fresh look.

      First, let's look at it in terms of horizontal overlap:

      HTML5 avoider game tutorial

      Here, the avatar and the enemy are not overlapping. We have: avatarX < avatarX + 30 < enemyX < enemyX + 30

      HTML5 avoider game tutorial

      Here, the avatar and the enemy are overlapping. We have: avatarX < enemyX < avatarX + 30 < enemyX + 30

      HTML5 avoider game tutorial

      Still overlapping. We have: enemyX < avatarX < enemyX + 30 < avatarX + 30

      HTML5 avoider game tutorial

      No longer overlapping. We have: enemyX < enemyX + 30 < avatarX < avatarX + 30

      Taking all these together, we can see that the enemy and avatar are overlapping horizontally if:

      avatarX < enemyX and enemyX < avatarX + 30

      ...or:

      enemyX < avatarX and avatarX < enemyX + 30

      In other words, for horizontal overlap, this condition must be true:

      (avatarX < enemyX && enemyX < avatarX + 30) || (enemyX < avatarX && avatarX < enemyX + 30)

      (Remember, && means "and"; || means "or".)

      It's not hard to come up with a similar condition for vertical overlap:

      (avatarY < enemyY && enemyY < avatarY + 33) || (enemyY < avatarY && avatarY < enemyY + 30)

      (We use 33 here because the avatar is 33 pixels tall, but only 30 pixels wide.)

      For the enemy and avatar to be overlapping, they must be overlapping both horizontally and vertically - so we use the && operator to combine these two conditions:

      ( (avatarX < enemyX && enemyX < avatarX + 30) || (enemyX < avatarX && avatarX < enemyX + 30) ) && ( (avatarY < enemyY && enemyY < avatarY + 33) || (enemyY < avatarY && avatarY < enemyY + 30) )

      That's a long condition! But it's actually quite simple, now that we've broken it down. Let's put it into our code. First, delete the old collision detection code from handleMouseMovement(); we'll put this in handleTick(), after we've moved and redrawn the enemy and avatar, so that it seems fairer.

      To start with, we'll just check for a collision between the avatar and the first enemy:

      
      
      function handleTick() {
      
      	//...
      
      	if ( ( (avatarX < enemyXPositions[0] && enemyXPositions[0] < avatarX + 30) || (enemyXPositions[0] < avatarX && avatarX < enemyXPositions[0] + 30) ) && ( (avatarY < enemyYPositions[0] && enemyYPositions[0] < avatarY + 33) || (enemyYPositions[0] < avatarY && avatarY < enemyYPositions[0] + 30) ) ) {
      		alert("You hit the first enemy!");
      	}
      }
      

      It looks messy (so you should probably add a comment to remind yourself of how it works later), but it's got everything we need. Does it work?

      Try it out here!

      It does work! Now we should make it work for all the enemies - and of course we can use a loop to do that:

      
      
      	currentEnemyNumber = 0;
      	while (currentEnemyNumber < numberOfEnemies) {
      		if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) {
      			alert("You hit an enemy!");
      		}
      		currentEnemyNumber = currentEnemyNumber + 1;
      	}
      

      Note that this goes inside handleTick(), at the end, so we have to reset currentEnemyNumber to 0. We also need to change the alert box text, since it might not be the first enemy that causes the alert to appear.

      Try it out here!

      All right, this is really shaping into a game! Okay, sure, the alert box is kind of annoying, but it serves our purposes for now.

      There's one more big addition I'd like us to make in this part...


      Make Infinitely Many Enemies

      We can add more and more enemy positions to our arrays - a hundred, a thousand, whatever - but eventually the supply will run out, and the player will have no more enemies to dodge. We need to be able to create new enemies and add their positions to the arrays while the game is running.

      When we want to change the value of a specific item inside enemyXPositions, it's easy: we just write enemyXPositions[3] = 100, or whatever. But how can we add something to the array? Writing enemyXPositions = [100] (or whatever) will just replace the array with a new one, containing just one item.

      The answer is in the arrays' .push() function; this allows us to add an item to an array without creating a new one. To demonstrate this, let's delete all the items from our arrays, and then use .push() to add new ones:

      
      
      var enemyYPositions = [];	//empty square brackets means new empty array
      var enemyXPositions = [];
      var avatarX = 0;
      var avatarY = 0;
      var avatarImage;
      var enemyImage;
      
      function setUpGame() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	avatarImage = new Image();
      	enemyImage = new Image();
      	enemyImage.src = "img/enemy.png";
      	avatarImage.src = "img/avatar.png";
      
      	enemyYPositions.push(0);
      	enemyXPositions.push(250);
      
      	gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
      
      	gameCanvas.addEventListener("mousemove", handleMouseMovement);
      	setInterval(handleTick, 25);
      }
      
      HTML5 avoider game tutorial
      Click to try it out.

      It works fine; the enemy still moves and the collision detection still works. It's exactly the same as if we'd started the code with:

      
      
      var enemyYPositions = [0];
      var enemyXPositions = [250];
      

      So what happens if we push the new values onto the arrays inside handleTick(), rather than inside setUpGame()?

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      	var numberOfEnemies = enemyXPositions.length;
      
      	enemyYPositions.push(0);
      	enemyXPositions.push(250);
      
      HTML5 avoider game tutorial
      Click to try it out.

      Hm. It's creating new enemies in the same positions at such a fast rate that they're overlapping. (The one at the top of the screen appears to be above all of the others because that's the last one to get drawn.)

      Let's try to fix this by creating the enemies at random starting x-positions. Remember that Math.random() gets us a random number between 0 and 1, so to get a random number between 0 and 400 - the width of the canvas - we can use Math.random() * 400:

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      	var numberOfEnemies = enemyXPositions.length;
      
      	enemyYPositions.push(0);
      	enemyXPositions.push(Math.random() * 400);
      
      HTML5 avoider game tutorial
      Click to try it out.

      Argh!

      All right, so maybe they're still coming in at too fast a rate...

      Less Enemies Per Second, Please

      Right now, the enemies are being created at a rate of one new enemy per tick - and since a tick is 25ms, there are 40 ticks per second, and therefore 40 new enemies per second.

      Let's reduce this to something more manageable: about two enemies per second.

      Since that's 1/20th of our current rate, we could achieve this by keeping track of how many ticks have passed, and creating a new one on tick number 20, tick number 40, tick number 60, and so on. But I think it'll be more fun if instead we make there be a 1/20 chance of a new enemy being creates on any given tick. This way, sometimes we'd create more than two new enemies in one second, and sometimes we'd create less, but it'd average out to two per second. The uncertainty involved would make the game a little more exciting (though perhaps "exciting" is a poor choice of words at this early stage of the game's development...).

      How can we do this, then? Well, we know that Math.random() create a random number between 0 and 1. Since these random numbers are evenly spread out between 0 and 1, that means there's a 1/20 change of the number generated being somewhere between 0 and... well, 1/20.

      In other words, the chance of Math.random() < 1/20 being true is 1/20.

      So, let's change our code to make use of this fact:

      
      
      function handleTick() {
      	var gameCanvas = document.getElementById("gameCanvas");
      	var currentEnemyNumber = 0;
      	var numberOfEnemies = enemyXPositions.length;
      
      	if (Math.random() < 1/20)
      	{
      		enemyYPositions.push(0);
      		enemyXPositions.push(Math.random() * 400);
      	}
      
      HTML5 avoider game tutorial
      Click to try it out.

      Much better! Feel free to experiment with that condition to find a value that works for you.


      Wrapping Up

      That's it for this part of the series. We've now built a rudimentary game - not a polished game, not a particularly fun game, but a game nonetheless.

      If you'd like to challenge yourself before the next part, have a go at making these changes:

      • Easy: The enemies 'pop' onto the top of the canvas; make them slide in, instead.
      • Easy: Some of the enemies are created partially off the side of the canvas; stop this happening.
      • Medium: The enemies all move at exactly the same speed, which is pretty dull; allow them to have different speeds.
      • Hard: The avatar can move off the right edge of the canvas, making it impossible for any enemies to touch it (assuming you've completed the second easy challenge). Make sure the avatar stays inside the boundaries.

      Enjoy!


    2. Michael James Williams says:
      October 30, 2011 at 1:39 am

      It’s quiz time! This month, let’s see how well you know your acronyms. AS3, OOP, HTTP, OMG!


      Let’s Get Quizzy


      Just So You Know…

      This quiz was built with the jQuizzy Quiz Engine by Siddharth, ace reviewer for Envato. jQuizzy is available for purchase over on Codecanyon :)



      Thanks also to Orman Clark and MediaLoot for their graphical contributions to the Activetuts+ Coffee Break Quizzes.


      What Would You Like To Be Tested On?

      If you’ve got an idea for an Activetuts+-related quiz subject, let us know in the comments!


    3. David Appleyard says:
      October 30, 2011 at 2:12 am

      We love the community of readers we have at Tuts+ and your opinion is incredibly valuable. This week, we’re giving you the chance to have your say about Tuts+, and win a few fantastic prizes at the same time! Read on to find out how to get involved and stand a chance of winning a copy of Adobe Creative Suite…


      The Prizes!

      We have a few fantastic prizes up for grabs, and the winning entries will be chosen at random when the survey closes in one week. Here’s what you stand a chance of winning:


      1st Prize: 1 x Adobe Creative Suite 5.5 Web Premium

      This prize has been kindly donated by Business Catalyst. Adobe Creative Suite 5.5 Web Premium software provides everything you need to create and deliver standards-based websites with HTML5, jQuery Mobile, and CSS3 support. Design immersive digital experiences and apps across desktops, smartphones, tablets, and televisions.

      You’ll find plenty of incredible software inside, including Photoshop CS5 Extended, Illustrator CS5, Flash Professional CS5.5, Dreamweaver CS5.5, and Fireworks CS5. It’s the perfect accompaniment to everything we teach at Tuts+!


      Runner-Up Prize: 4 x Six Month Tuts+ Premium Memberships

      Four lucky runners up will receive a six month subscription to our Tuts+ Premium membership programme, completely free of charge! This is a brilliant way to expand your knowledge in different creative areas, and access a regular selection of incredibly high-quality tutorials and resources. You’ll be a pro in no time!


      Competition Sponsored by Business Catalyst

      Business Catalyst is a Adobe’s new hosted service for building business websites, replacing 5+ separate tools with one integrated solution.

      Incorporating web hosting, content management, eCommerce features, email marketing and built in customer management tools, Business Catalyst can help you accelerate your projects and increase your profit margins. Try it out for free at businesscatalyst.com.


      Complete the Survey

      It just takes a few minutes to complete the survey, and we really value your opinion. We’ll be using the information from our readers to make the Tuts+ network better than ever, so this is your opportunity to let us know what you’d like to see change on the sites!

      Take the Survey & Enter the Competition


      Competition Rules

      • Please don’t enter more than once – if you do, none of your entries will count.
      • The winners will be picked randomly and contacted by email.
      • The competition closes in exactly one week. Any entries after that time period won’t count.
      • We’d love to hear your honest feedback, so feel free to say exactly what you think!

      Thanks for taking the time to share your thoughts about the Tuts+ network. We really appreciate it, and look forward to hearing what you have to say!


    4. Dru Kepple says:
      October 30, 2011 at 2:20 am

      For this chapter of AS3 101, we will be diving into the mechanics of the Flash event system. If you’ve been following along so far, you’ll have seen events in use, dating all the way back to the first episode of the series. The editor and I felt that it was time to write up something to be formally included in the curriculum, so if you’ve ever seen those lines of code about adding event listeners or dispatching events, and not quite caught on, then this is the tutorial for you.

      There already exists an Activetuts+ tutorial on the basics of Events, so the focus of this tutorial will be on dispatching events from your own classes, including creating custom event types and objects.

      To be successful with this tutorial, you should feel comfortable with writing and using your own classes in ActionScript 3, as well as feeling secure with using the existing events provided by Flash, such MouseEvent.CLICK or Event.ENTER_FRAME. We will be focusing primarily on dispatching custom events from custom classes.


      Preview

      We’ll spend a lot of time on the theory for this tutorial, but in the end we’ll build a simple slider control that dispatches its own events:


      Step 1: Why Use Event Dispatching?

      This example is actually a rather simple example of why you’d want to dispatch your own events. When you write your own classes, you are ideally keeping them in their own black boxes and encapsulated. But you still need to have the various objects interoperate in order to create a useful program.

      The event model provided by ActionScript 3 is a rather good and convenient way to facilitate communication between your classes, while maintaining a separation of responsibilities in your classes. So if we write our own custom class, such as the ActiveSlider class showcases above, we have a need to enable other objects to be aware of when the slider changes its value. If the slider can dispatch its own change event, then other objects that need to know that information can easily subscribe and be notified.

      Personally, I find the need to dispatch my own events in my classes so common that my class template sets up each new class with the boilerplate it needs to be able to do so. As you learn to keep objects discrete, you’ll turn to event dispatching as the most common technique to do so.


      Step 2: How to Dispatch Your Own Events

      I have good news: dispatching your own events is actually very simple. It is one of the core tenets of ActionScript 3, built in to the Flash Player, and as such there is only one thing you need to do in order to gain the ability to dispatch events. This one thing is:

      Extend EventDispatcher

      That’s it: when writing your class, use the line:

      
      
      public class MyClass extends EventDispatcher {
      

      Of course, you need to import EventDispatcher, which is in the flash.events package. You will most likely need other classes in the package, so it might be most convenient to simply import the package with a wildcard.

      
      
      import flash.events.*;
      

      Step 3: Dispatch

      Now you’re set up to dispatch an event. All you need to do now is call a method provided by EventDispatcher named dispatchEvent. Easy to grasp, no?

      When you call dispatchEvent, you must provide at least one argument, an Event object. All built-in Event objects are in the flash.events package, so here’s where that wildcard import comes in handy. Each type of Event object will have its own requirements, but most often you simply need to pass it just one argument, as well. This argument is the event type, which is a String that names the event, like "click" or "complete". These are more commonly written as MouseEvent.CLICK or Event.COMPLETE, but the end result is the same; it’s an identifier that separates one type of event from another, and allows one Event object to manage multiple event types.

      So, putting it all together, if you wanted to dispatch a "complete" event, you could do so like this:

      
      
      dispatchEvent(new Event(Event.COMPLETE));
      

      Just drop that line (or one like it) in whichever method it’s appropriate in your class. Your class will then utilize its inherited event dispatching system and any listeners will be notified for you. Speaking of listeners, let’s take a brief look at those, as well.


      Step 4: Listen

      Any other object in your application can listen for your custom events now. More good news: this is no different than registering for events for the built-in classes. In the previous step, we set up our hypothetical class to dispatch a COMPLETE event. To listen for that event, we could write this line somewhere else in our program:

      
      
      var myObject:MyClass = new MyClass();
      myObject.addEventListener(Event.COMPLETE, myCompleteHandler);
      function myCompleteHandler(e:Event):void {
          trace("My object completes me.");
      }
      

      And that’s it. This should look familiar to anyone who has hooked up a COMPLETE listener to a Loader, for instance, so I won’t dwell any further on this.


      Step 5: Where to Dispatch

      Where you actually place the dispatchEvent line of code requires some consideration. Typically, it should be the last line of code of the method in which it is written. This is so that any other code that also runs in that method can set properties or otherwise update the internal state of the object. By dispatching after this internal update is complete, the object is in a “clean” state at the time of the dispatch.

      Consider, for example, our working example. Let’s say the COMPLETE event is all about the processing of some data; a bunch of data so large that it will take several seconds to completely process, so the object’s purpose is to handle the processing asynchronously so as not to block the UI. And we’re dispatching the COMPLETE event as a way of saying that the data has been processed.

      Now suppose that the main method in question looks something like this:

      
      
      private function processDataChunk():void {
          _data += someDataProcess();
          if (done()) {
              closeData();
          }
      }
      

      OK, not very realistic, but it illustrates the point. We keep building up the internal data until some other internal logic determines we’re done, at which point we then write out some final bits of data to close the operation.

      Now, let’s add the dispatchEvent call:

      
      
      private function processDataChunk():void {
          _data += someDataProcess();
          if (done()) {
              dispatchEvent(new Event(Event.COMPLETE));
              closeData();
          }
      }
      

      What’s the issue with this approach? Any code that executes within listeners for the COMPLETE event will run before the closeData method is called. Therefore, the state of the dispatcher changes more than once within the span of the processDataChunk method, and is not “stable” until after the closeData call. Yet, we tell all of our listeners that we’re complete before that call. This could lead to some hard-to-track-down bugs where one object claims to be COMPLETE but really isn’t. The obvious solution is to switch some lines around:

      
      
      private function processDataChunk():void {
          _data += someDataProcess();
          if (done()) {
              closeData();
              dispatchEvent(new Event(Event.COMPLETE));
          }
      }
      

      Step 6: Custom Events

      You’re all set up to dispatch your own events. Now, what should you dispatch? There are a few options to consider:

      1. Simply reuse an event object and event type already provided by the Flash Player
      2. Reusing an existing event object, but provide a custom event type
      3. Re-Dispatch an existing event
      4. Create a custom event object
      5. Push vs. pull

      This first option we’ve already seen in our previous examples. We have a need to dispatch an event related to the completion of some process, and as it happens Flash provides an event type (COMPLETE) associated with an event object (Event) that fits our criteria. We have no need to provide additional data with the event. Dispatching an Event.COMPLETE event is all we need.

      We’ll explore these other options in the forthcoming steps.


      Step 7: Custom Event Types

      As hinted at back in the “Dispatch” step, event types are simply String identifiers. They can technically be any String you like. It’s usually sufficient to make it a single word (like “complete” or “click”) or very short phrase (like “ioError” or “keyFocusChange”); it only has to be unique within a given event dispatcher’s realm of available events.

      In addition, Event objects (including subclasses, like MouseEvent or ProgressEvent) don’t really care what event type they are given when instantiated. An EventDispatcher will gladly dispatch events of any type identifier and of any class (as long as it’s the Event class or a subclass).

      The upshot of this is that you can make up your own event type String, dispatch it, and set up listeners with it, and everything will be just fine. This is helpful when you want to dispatch an event but can’t necessarily find a good representation of the nature of the event in the built-in classes.

      As an example, you might have a class that acts as a coordinator for several things at once: load some XML, load some images based on the XML data, and create a layout based on the loaded images’ sizes, ready for an initial animation, at which point you’d like to dispatch an event. While the COMPLETE event might be suitable, you may feel that a “ready” event more appropriately encapsulates the meaning.

      This is as simple as deciding on the String to use, and then using it. Use it both when adding listeners, and when dispatching the event. If the String matches, the event will get to where it needs to go. For example, this is a partial listing of a hypothetical class:

      
      
      public class MyClass extends EventDispatcher {
      
          // ... A bunch of other stuff not shown.
      
          private function determineReadiness():void {
              if (everythingIsReady) {
                  dispatchEvent(new Event("ready"));
              }
          }
      
      }
      

      And code from elsewhere in the same program:

      
      
      var myObject:MyClass = new MyClass();
      myObject.addEventListener("ready", onObjectReady);
      
      function onObjectReady(e:Event):void {
          // Do stuff now that it's ready.
      }
      

      And that would work.

      However, it’s worth mentioning while we’re here that typing in matching Strings all over the place is not a good practice. The possibility for error is high, and the event system won’t tell you that you’ve typed "raedy" instead of "ready". The fact that the event system is flexible and simple to use – just pass it any old String for the event type – is also something of a weakness. Your dispatcher will gladly accept a listener for anything, even a "raedy" event. It doesn’t really reconcile what event types are registered against what event types are actually dispatched.

      To help prevent this, the standard approach is to simply put the String you want to use in a static constant somewhere, and then never use that literal String again. Only use the constant. Of course, the possibility of typos is just a great as before, but if you’re using a READY constant and not the "ready" literal String, a mistype will trigger a compiler warning. You’ll be able to correct your error quickly and easily. A mistype with the literal Strings produces no compiler error, nor does it produce a run-time error. The only thing that happens is that the SWF does not seem to work properly, because the event listener doesn’t fire.

      With this in mind, it’s most common to store these constants on the related Event class. We’ll get to custom Event classes in just a few steps. But in the situation outlined in this step (i.e., we’re reusing an Event class but not an event type), I find it more convenient to simply store that constant on the dispatcher class. So we might choose to do this:

      
      
      public class MyClass extends EventDispatcher {
      
          public static const READY:String = "ready";
      
          // etc.
      
          private function determineReadiness():void {
              if (everythingIsReady) {
                  dispatchEvent(new Event(READY));
              }
          }
      
      }
      

      And:

      
      
      var myObject:MyClass = new MyClass();
      myObject.addEventListener(READY, onObjectReady);
      
      function onObjectReady(e:Event):void {
          // Do stuff now that it's ready.
      }
      

      This gives us the safety of storing event types in constants, while not forcing the inconvenience of creating a whole Event class that we don’t need. I must stress that this is a stylistic choice, and you may feel free to use this technique or not. I felt it warranted explanation, so that you could make your own informed decision. In either regard, you should definitely store your custom event type Strings in static constants. Where those static constants are defined is up to you.


      Step 8: Re-Dispatching Events

      There are several times when one class (let’s call it ClassX) owns a property that is typed as another class (we’ll call that one ClassY), while itself being owned by a third class (how about ClassZ?). ClassX is listening for an event from ClassY, but not only do we want to have ClassX respond to the event, we also want to consider that ClassX should dispatch a similar (or even the same) event so that ClassZ can also take further action.

      As a more concrete example, we have a class (this will be “ClassX“) that is a sort of data manager. It loads an XML document, parses it, and stores data from the XML in its own properties. So it has an URLLoader object (this would be “ClassY“), and listens for the Event.COMPLETE event for when the XML document is loaded.

      Then we have a main document class that owns the data manager (the document class is “ClassZ“). It’s coordinating the data loading with other UI elements, so it wants to know when the data is loaded and ready, so it can proceed to create and layout UI elements based on the data.

      
      
      public class DataManager extends EventDispatcher {
          private var _xmlLoader:URLLoader;
          public function DataManager() {
              _xmlLoader = new URLLoader(new URLRequest("some.xml"));
              _xmlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
          }
          // ... A bunch of other stuff
          private function onLoadComplete(e:Event):void {
              // ... XML parsing
              // With the XML parsed, we'd like to dispatch another event to signal being done.
          }
      }
      

      We could do this:

      
      
      dispatchEvent(new Event(Event.COMPLETE));
      

      But we could also do this:

      
      
      dispatchEvent(e);
      

      Here we simply re-dispatch the existing event. Not only do we reuse the event type and the Event class, but we’re actually reusing the entire Event object as it was passed in to our own listener.

      It’s not rocket science, but it is a handy little technique that is surprisingly not so obvious.

      “But wait,” you must be thinking, “if we redispatched an event that originated from the URLLoader object, wouldn’t the target of the event still be _xmlLoader when it gets back out to the document class?” And you would have a very good and thoughtful point, and I would be proud of you for thinking so studiously, but you would be wrong.

      A rather magical thing happens when redispatching events. The target property gets set to the current dispatcher. You can find a working example of the code in this step in the download package, entitled redispatch.

      Actually, it’s not all that magical. When calling dispatchEvent, if the Event object that is passed in already has a target set, then the clone method is called on the Event, creating an identical but discreet copy of the original Event, except for the value contained in target.


      Step 9: Custom Event Objects

      Everything mentioned so far is something you will want to know. But there will come a point when the best thing to do is to dispatch your own custom event. Not just a custom event type, but a whole custom Event class.

      The process for doing this is straightforward, you just need to follow a few steps. We’ll discuss these in due course. Note that quite a bit of this is boilerplate code, and you could easily create a template for an Event subclass and change just a few key pieces and be off and running. The general steps, in shortened-as-if-you-knew-what-I-was-talking-about form:

      1. Subclass Event
      2. Call super(...)
      3. Store event types in public static constants
      4. Declare private properties to hold custom data
      5. Create public getters to provide read-only access to the custom info
      6. (Optional) Override the clone method
      7. (Optional) Override the toString method

      To explain these process more deeply, we’ll get a start on our slider project and create the SliderEvent we will need for that. So we need to start our project before we can write some code, so a quick diversion in the next step, then we’ll start writing a custom Event class.


      Step 10: Create the Project Structure

      We’ll keep things pretty simple for this one, but we’ll nonetheless create packages for our classes.

      Start by creating a folder for the entire project. Mine will be called slider.

      Inside of this, create a com folder, and inside of that, an activetuts folder.

      Now create two folders inside of activetuts: events and ui. Your final folder structure should look something like this:

      • slider
        • com
          • activetuts
            • events
            • slider

      Now back to our Event class.


      Step 11: Subclass Event

      First, create a new text file in the slider/com/activetuts/events folder, and call it SliderEvent.as. We’ll pop in the boilerplate for any class first:

      
      
      package com.activetuts.events {
      
          public class SliderEvent {
      
              public function SliderEvent() {
      
              }
      
          }
      
      }
      

      There should be nothing surprising here, and if you have ActionScript templates for your text editor, you shouldn’t even have to type that much in.

      Now, we’ll modify this so that it extends Event.

      
      
      package com.activetuts.events {
      
          import flash.events.Event;
      
          public class SliderEvent extends Event {
      
              public function SliderEvent() {
      
              }
      
          }
      
      }
      

      As you can see, we simply import the Event class, add extends Event to the class definition.


      Step 12: Call super

      Our superclass can handle a lot for us, which is great, but we do need to make sure we initialize the superclass properly when we initialize our subclass. We need to set up the constructor with arguments matching those found on Event‘s constructor, and pass those along with a call to super.

      
      
      package com.activetuts.events {
      
          import flash.events.Event;
      
          public class SliderEvent extends Event {
      
              public function SliderEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=true) {
                  super(type, bubbles, cancelable);
              }
      
          }
      
      }
      

      This is, so far, basic subclassing techniques. In fact, depending on your editor’s prowess with templates, you may be able to specify a super class when you create the file and have all of this done for you. Flash Builder, for example, is able to do this.


      Step 13: Store Event Types in Public Static Constants

      Presumably, there will be one or more event types associated with this event class. Just like the COMPLETE event is associated with the Event class, and the CLICK even with the MouseEvent class, our custom event class will likely have custom event types.

      This is as simple as writing a line like the following for every event type you would like to add:

      
      
      public static const EVENT_TYPE:String = "eventType";
      

      Let’s do that now for the SliderEvent class.

      
      
      package com.activetuts.events {
      
          import flash.events.Event;
      
          public class SliderEvent extends Event {
      
              public static const CHANGE:String = "sliderChange";
      
              public function SliderEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=true) {
                  super(type, bubbles, cancelable);
              }
      
          }
      
      }
      

      We could theoretically use our class now. We can use the SliderEvent in dispatchEvent, and listen for and create events with the SliderEvent.CHANGE event type.

      But we won’t stop there. There is more to consider. But before we do more code-writing, we need to take another detour into theory.


      Step 14: Push vs. Pull

      When an event is dispatched, sometimes it’s enough to simply know that the event has occurred. For example, most times that you are interested in Event.ENTER_FRAME, Event.COMPLETE, or TimeEvent.TIMER events, you probably just want to know that the event happened. There are other times, though, when you probably want to know more. When listening to MouseEvent.CLICK, you might be interested in whether the Shift key was held down, or the coordinates of the mouse at the time of the click. If you’re listening to ProgressEvent.PROGRESS, you will most likely want to know the actual progress of the load; that is, how many bytes have loaded and how many there are to load in total.

      The difference between these two methodologies is sometimes known as “push” and “pull.” Those terms refer to how the event listener gets data related to the event. If the data is “pushed” then there is data stored within the event object, and in order to get the data the listener merely need to use properties on the event object. If data is to be “pulled,” though, generally the event object has very little information contained within – just the necessities: the type, the target, etc. This target, though, is indispensable, as it provides access to the event dispatcher to the event listener, allowing the listener to get the data it needs from the dispatcher.

      In other words, you can either push a bunch of data to the listener inside the event object, or you can require the listener to pull the data out of the dispatcher as needed.

      The pros and cons of each technique are somewhat balanced, in my opinion, and the path you choose for your event object depends on the situation at hand, and not a little bit on personal preference.

      Exhibit A:

        Pros Cons
      Push
      • Data is easily accessible in the event object
      • You may be pushing data that is unneeded. Bloating the event object with a bunch of data that is rarely used could lead to memory and/or performance issues.
      Pull
      • Very easy to write. You probably don’t need a custom Event class to execute a pull event.
      • Very easy to use. If it’s just an Event class, the only required argument is the event type.
      • If data commonly pulled out of the dispatcher is expensive to compute and return, you may be taking a hit on performance by requiring the dispatcher to continually hand out that information.
      • Some data might be difficult to pull, e.g. the KeyboardEvent has a keyCode property to detail the key that was pressed to trigger the event.

      This might be a good discussion for the comments; I’m sure many of you reading have passionate feelings about which methodology is better. Personally, I try to find the method that works best for the situation.

      Having said that, it’s worth noting that to this point our SliderEvent class is rather “pull-ish.” For the sake of illustration, and because it’s not a terrible idea (though I did come up with several of those), we’ll continue on with making this an event that pushes data along with it; namely the value of the slider when it was changed.


      Step 15: Declare Private Properties to Hold Custom Data

      To implement a push event, we need to have a place to store the data being pushed. We’ll add a private property for that purpose.

      You should still have SliderEvent open (if not… what are you waiting for?). Add the highlighted line:

      
      
      package com.activetuts.events {
      
          import flash.events.Event;
      
          public class SliderEvent extends Event {
      
              public static const CHANGE:String = "sliderChange";
      
              private var _sliderValue:Number;
      
              public function SliderEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=true) {
                  super(type, bubbles, cancelable);
              }
      
          }
      
      }
      

      Next we’ll modify the constructor so that we can accept a value parameter, and set the private property with that:

      
      
      public function SliderEvent(type:String, sliderValue:Number, bubbles:Boolean=false, cancelable:Boolean=true) {
          _sliderValue = sliderValue;
          super(type, bubbles, cancelable);
      }
      

      This way we can easily create the SliderEvent and set up its push data in one line.

      Why use private properties? In this case, we want to protect the data. In my opinion the data related to an event is immutable, so long as it’s associated with the event. Being able to change the data of an event object is like editing a history textbook. To be fair, this is my opinion and the standard used by Adobe with their built-in classes is to use writable properties (technically they use private properties and public getters and setters, but the end result is the same).


      Step 16: Create Public Getters for the Custom Data

      So the next step would be to make sure we can access the data being pushed. A private property by itself is not useful to this purpose. Therefore, we need to write a public getter for the _sliderValue property. We will choose to not write a setter, so that the property becomes read-only (as discussed in the last step).

      Add this method to the class:

      
      
      public function get sliderValue():Number {
          return _sliderValue;
      }
      

      This adds a getter so we can access the sliderValue in property-like fashion. I’m choosing not to add the matching setter. You can add one if you feel it’s worthwhile.


      Step 17: Override the clone method

      I mentioned the clone method a little while ago. You probably won’t call clone much yourself, but it’s not a bad idea to override the clone method so that your custom event plays nicely with the event system.

      Add this method to your class:

      
      
      override public function clone():Event {
          return new SliderEvent(type, sliderValue, bubbles, cancelable);
      }
      

      First, notice the signature of this method. We’re using override because this method is declared in Event, and we’re inheriting it. It also returns an object of type Event. Make sure, when writing your clone override, that you put the correct return type in. It’s easy to forget and put the type of your class there, but that will cause an incompatible override error, because the return types need to match.

      All we’re really doing in the meat of the event is creating a new SliderEvent and passing in the same values that we have stored in the current event object. This creates an identical but discreet copy: a clone.

      This is an optional step, but it’s a quick win and ensures that your custom event plays well with the rest of the event system.


      Step 18: Override the toString method

      One last thing, and again this is optional. But it’s also very useful as a debug tool, so it usually pays for itself within a few uses.

      In case you haven’t been told yet, the toString method exists on all objects (it’s declared and defined in Object, the über-class from which all other classes inherit, whether you like it or not). It can be called explicitly, but the handy thing is that it get called automatically in a number of cases. For example, when you pass object to the trace function, any object that is not already a String will have toString called on it to make sure it is formatted nicely for the Output panel. It even gets called when working with Objects together with Strings, as with concatenation. For example, if you write this:

      
      
      "The answer to life, the universe, and everything is " + 42;
      

      ActionScript is smart enough to convert 42 into a String representation of the Number before concatenating the String. Trying to add a String and a Number is bad news, but converting a Number to a String and then concatenating it with another String is just fine.

      So when you write your own classes, you can provide a toString method, which takes no arguments and returns a String, and return whatever String you like.

      In the case of Event objects, Adobe helpfully provides a formatToString method to help all Events look similar when traced. We’ll use it in the method we’re about to add to our class:

      
      
      override public function toString():String {
          return formatToString("SliderValue", "type", "sliderValue");
      }
      

      First, note the method signature. Again, it’s an override so we have that keyword. It’s public, it takes no parameters, and returns a String (which should be obvious).

      Next, note the single line in the method body. We call formatToString, which is defined in Event, so it’s easy to use. The first argument we pass to it is the String name of the class. After that, the arguments are open-ended. You can pass in one, 15, or none. We’re passing in two. No matter how many you pass in, they should all be Strings, and they should match property names on your class. "type" is defined by Event, but "sliderValue" is defined by our own class. Either way, what happens is that the name of the property is printed, followed by an equals sign, which is followed by the actual value of that property. In short, it will end up looking like this:

      
      
      [language="text"]
      [SliderValue type="sliderChange" sliderValue=0.34146341463414637]
      

      This is entirely non-functional but very useful. It can provide a quick glimpse into the event when things aren’t working the way you think they should.


      Step 19: Building the Slider

      At this point, we’ve been through the key concept of this tutorial: writing a custom Event class. But we really need to put it to the test. We’ll spend the remainder of our time building the simple slider application that was previewed at the beginning of the tutorial.

      We already have a project folder structure; we just need a few more files. We’ll start with the FLA file.

      Create a new Flash file (ActionScript 3.0, of course) and save it as ActiveSlider.fla in the root of your project folder. I’m going to assume that you don’t need step-by-step details on how to put this simple FLA together, and instead I’ll lay out the key elements. You can use the FLA file found in the start folder of the download package for reference, as well, or even just copy that FLA to your project folder and call this step done.

      There are three main objects on the stage.

      1. The slider track. This is a long, narrow strip that indicates to where the slider can be moved. The slider moves “in” the track.
        • Needs to be a Movie Clip
        • For easiest math, should have artwork arranged so that the registration point is at the top left corner
        • Name it track_mc
        • Place in the upper center; it should take up most of the width of the stage and have space below it.
      2. The slide grip. This is a button-sized element that indicates the current position of the slider. It’s the piece that moves along the track and responds to the mouse.
        • Needs to be a Movie Clip.
        • Again, for math, should have the registration point at the top left
        • Name it grip_mc
        • Place it over the track, so that it is vertically centered with the track, and somewhere within the left and right ends of the track
        • It should be stacked on top of the track, so that the grip obscures the track (put it on a layer higher)
      3. The output field. This is a text field that, for our own demonstration purposes, displays the current value of the slider.
        • Needs to be a dynamic Text Field.
        • Name it output_tf
        • Fonts are inconsequential; set it to whatever you like and embed as necessary
        • Place it in the lower portion of the stage, so that it doesn’t conflict with the space required by the slider.
      The stage of our FLA

      Other than hooking up the document class, which we’ll write in two steps, the FLA is ready for business.


      Step 20: The ActiveSlider Class

      The main UI class with which we’ll work is the ActiveSlider class. This will extend EventDispatcher, target the two Movie Clips on the stage, and set up mouse interactivity for slider behavior. Most exciting of all, it will dispatch a SliderEvent.

      Start by making a new class file called ActiveSlider.as in the com/activetuts/slider folder of your project. This class isn’t too intense (at least, not for our purposes here. A slider class could get much more involved), and I’ll just present the code in full and discuss as we go:

      
      
      package com.activetuts.slider {
      
          import flash.display.*;
          import flash.events.*;
          import flash.geom.*;
      

      Nothing exciting, just setting up the package and imports.

      
      
          public class ActiveSlider extends EventDispatcher {
      
              private var _track:Sprite;
              private var _grip:Sprite;
              private var _grabOffset:Point;
      

      We’ll need these three properties. The first two keep track of the Sprites (or MovieClips) that make up the slider. The third is used while dragging the slider grip; it helps keep the grip’s position offset from the mouse by an amount relative to where the grip was clicked in the first place.

      
      
              public function ActiveSlider(track:Sprite, grip:Sprite) {
                  _track = track;
                  _grip = grip;
                  if (_grip.parent != _track.parent) {
                      throw new Error("The track and the grip Sprites are not in the same container.")
                  }
      
                  _grip.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
      
                  if (_grip.stage) {
                      addStageListener();
                  } else {
                      _grip.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
                  }
              }
      

      This is the constructor. It accepts two Sprite arguments, which get passed on to the first two of those properties for storage. It then does a simple check to make sure the two Sprites are in the same coordinate space by checking that their parent properties reference the same object. If they don’t, then our math for placing the grip might be unreliable, so we throw an error as a way of alerting the developer. The rest of the constructor is devoted to adding two event listeners. The first is a MOUSE_DOWN event and straight forward. But the second is trying to add a MOUSE_UP event to the stage, which might or might not exist depending on whether the grip Sprite is on the display list or not. The next two methods might make this a little clearer:

      
      
              private function onAddedToStage(e:Event):void {
                  _grip.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
                  addStageListener();
              }
      
              private function addStageListener():void {
                  _grip.stage.addEventListener(MouseEvent.MOUSE_UP,   onUp);
              }
      

      The onAddedToStage method is an event listener for the ADDED_TO_STAGE event, which was set up in the constructor, but only if the grip Sprite did not already have a reference to the stage. The addStageListener method simply adds the MOUSE_UP event listener to the stage. So, if there is a stage reference in the constructor, we call addStageListener directly. If there is not a stage reference, we set up the ADDED_TO_STAGE event, and when the grip is added to the display list, and thus has a stage reference, the onAddedToStage method fires which then in turn calls addStageListener. It also removes the ADDED_TO_STAGE event listener, because we only need to do this once.

      
      
              private function onDown(e:MouseEvent):void {
                  _grabOffset = new Point(e.localX, e.localY);
                  _grip.addEventListener(Event.ENTER_FRAME, onFrame);
              }
      
              private function onUp(e:MouseEvent):void {
                  _grip.removeEventListener(Event.ENTER_FRAME, onFrame);
              }
      

      Next we have our two mouse event listeners. In onDown, the key line is to then add an ENTER_FRAME event listener. In onUp, we remove that listener. Also in onDown, we make note of where on the grip the mouse click actually happened, and store that in _grabOffset. This will play into our onFrame method next.

      
      
              private function onFrame(e:Event):void {
                  _grip.x = _grip.parent.mouseX - _grabOffset.x;
      
                  var gripBounds = _grip.getBounds(_grip.parent);
                  var trackBounds = _track.getBounds(_grip.parent);
      
                  if (gripBounds.x &lt; trackBounds.x) {
                      _grip.x = _track.x;
                  } else if (gripBounds.right &gt; trackBounds.right) {
                      _grip.x = trackBounds.right - gripBounds.width
                  }
      
                  trace(this.value);
              }
      

      This is the main meat of our slider logic. This method fires repeated on an ENTER_FRAME event, but only when the mouse has been pressed down on the grip, and only for as long as the mouse remains pressed.

      First, we set the grip’s x property to match the mouse position, only we need to offset it based on the original mouse position, so that it moves smoothly and doesn’t jump so that its left edge is at the mouse.

      The next two lines calculate the bounding rectangles of both the grip and track Sprites. We’ll use these rectangles in the upcoming math, so we’ll keep things tidier by pre-calculating the rectangles and storing them in variables.

      Then there is the if block. This just constrains our slider to within the track. It’s a simple check to see if the x of the grip, as calculated in the first line of the method, is lower than (to the left of) the x of the track. If it is, it would be too far, so we need to move the grip to that minimum value. Similarly, we check to see if the grip’s right edge is greater than (to the right of) the track’s right edge, and if so we need to reel it back in to that maximum value.

      Finally, we have a reliable grip position, and for now we just trace the current slider value, which is calculated in the final bit of code for the class:

      
      
              private function get value():Number {
                  return (_grip.x - _track.x) / (_track.width - _grip.width);
              }
      
          }
      
      }
      

      This is a simple getter, although the math being used for the return value might be a little confusing. It determines the current grip position as a percentage of the range of motion of the grip. It would be more obvious if it were just this:

      
      
      return _grip.x / _track.width;
      

      …which is reasonable but doesn’t take into account that the grip doesn’t actually travel the entire width of the track. It goes all the way to the left edge, but only as far right as the right edge of the track minus the width of the grip. So this is more accurate:

      
      
      return _grip.x / (_track.width - _grip.width);
      

      This makes the width by which we divide the range of motion. However, there may still be a gotcha in that the track may be offset to the left or right from its container, meaning that the values we get aren’t quite right. We need to neutralize that by subtracting the x position of the track from the grip’s x, and we end up with this:

      
      
      return (_grip.x - _track.x) / (_track.width - _grip.width);
      

      For reference, here is the complete class, with my ramblings left out:

      
      
      package com.activetuts.slider {
      
          import flash.display.*;
          import flash.events.*;
          import flash.geom.*;
      
          public class ActiveSlider extends EventDispatcher {
      
              private var _track:Sprite;
              private var _grip:Sprite;
              private var _grabOffset:Point;
      
              public function ActiveSlider(track:Sprite, grip:Sprite) {
                  _track = track;
                  _grip = grip;
                  if (_grip.parent != _track.parent) {
                      throw new Error("The track and the grip Sprites are not in the same container.")
                  }
      
                  _grip.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
      
                  if (_grip.stage) {
                      addStageListener();
                  } else {
                      _grip.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
                  }
              }
      
              private function onAddedToStage(e:Event):void {
                  _grip.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
                  addStageListener();
              }
      
              private function addStageListener():void {
                  _grip.stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
              }
      
              private function onDown(e:MouseEvent):void {
                  _grabOffset = new Point(e.localX, e.localY);
                  _grip.addEventListener(Event.ENTER_FRAME, onFrame);
              }
      
              private function onUp(e:MouseEvent):void {
                  _grip.removeEventListener(Event.ENTER_FRAME, onFrame);
              }
      
              private function onFrame(e:Event):void {
                  _grip.x = _grip.parent.mouseX - _grabOffset.x;
      
                  var gripBounds = _grip.getBounds(_grip.parent);
                  var trackBounds = _track.getBounds(_grip.parent);
      
                  if (gripBounds.x &lt; trackBounds.x) {
                      _grip.x = _track.x;
                  } else if (gripBounds.right &gt; trackBounds.right) {
                      _grip.x = trackBounds.right - _grip.width
                  }
      
                  trace(this.value);
              }
      
              private function get value():Number {
                  return (_grip.x - _track.x) / ((_track.width - _grip.width) - _track.x);
              }
      
          }
      
      }
      

      We have not added in our SliderEvent class yet; we’ll take a separate step to do that. But first, we need our document class so we can actually use the ActiveSlider.


      Step 21: The Document Class

      We need one more file to make it work: our document class. Make a new class file named SliderDemo in the project root folder. Add in the following code:

      
      
      package  {
      
          import flash.display.*;
          import flash.events.*;
          import com.activetuts.slider.ActiveSlider;
      
          public class SliderDemo extends Sprite {
      
              private var _slider:ActiveSlider;
      
              public function SliderDemo() {
                  _slider = new ActiveSlider(track_mc, grip_mc);
              }
      
          }
      
      }
      

      This is much simpler that our ActiveSlider class. It really just sets up an ActiveSlider into the property named _slider.

      Go back to the FLA file, and enter SliderDemo into the document class field, and you should be able to try this out. The slider should be able to move back and forth, and constrain itself to the width of the track.

      Now for one last task. We need to dispatch, and listen for, a SliderEvent.CHANGE event. We’ll do that next.


      Step 22: Dispatching the SliderEvent

      Go back to the ActiveSlider class, and make a single-line change to the onFrame method:

      
      
      private function onFrame(e:Event):void {
          _grip.x = _grip.parent.mouseX - _grabOffset.x;
      
          var gripBounds = _grip.getBounds(_grip.parent);
          var trackBounds = _track.getBounds(_grip.parent);
      
          if (gripBounds.x &lt; trackBounds.x) {
              _grip.x = _track.x;
          } else if (gripBounds.right &gt; trackBounds.right) {
              _grip.x = trackBounds.right - _grip.width
          }
      
          dispatchEvent(new SliderEvent(SliderEvent.CHANGE, this.value));
      }
      

      We’ve removed the trace and replaced it with a real, live event dispatch. For this to work, though, we need to import the SliderEvent class:

      
      
      package com.activetuts.slider {
      
          import flash.display.*;
          import flash.events.*;
          import flash.geom.*;
          import com.activetuts.events.SliderEvent;
      

      Step 23: Listening For the SliderEvent

      Finally, go back to the SliderDemo class, and change it so it listens for and reacts to the SliderEvent:

      
      
      package  {
      
          import flash.display.*;
          import flash.events.*;
          import com.activetuts.slider.ActiveSlider;
          import com.activetuts.events.SliderEvent;
      
          public class SliderDemo extends Sprite {
      
              private var _slider:ActiveSlider;
      
              public function SliderDemo() {
                  _slider = new ActiveSlider(track_mc, grip_mc);
                  _slider.addEventListener(SliderEvent.CHANGE, onSliderChange);
              }
      
              private function onSliderChange(e:SliderEvent):void {
                  trace(e);
                  output_tf.text = e.sliderValue.toString();
              }
      
          }
      
      }
      

      We’re again importing the SliderEvent class, and after creating the ActiveSlider, adding a listener called onSliderChange to the slider. That method is the biggest addition, but still is a regular event listener. It’s much like any other event listener, only we’re making sure to type the event argument as SliderEvent, because that’s what we’re getting.

      The first line of code is a bit superfluous, but I wanted to see what happens when you trace our SliderEvent object. You’ll see, when you run this, a typical-for-Flash formatting of the event object.

      The event object represented as a String

      The second line does what we were after to begin with. It simply grabs the sliderValue property, turns it into a String, then sticks that String into the TextField on the stage, so we can see the slider value in the movie.


      Step 24: Wrapping Up

      When you start rolling your own custom events, you start working with ActionScript 3 the way it was meant to be used. Events help you decouple classes from each other, and a well structured event flow in your application can really make the difference between something that is easy to work with and something that is buggy and temperamental. With this (theoretically) final instalment of AS3 101, you should be well on your way to becoming a ninja.

      Thanks for reading, and I’ll see you back here on Activetuts+ before you know it!


    5. Daniel Apt says:
      October 30, 2011 at 2:44 am
      This entry is part 1 of 1 in the series Create a Microphone-Controlled Flash Game

      In this two-part mini series, you’ll learn how to create a Flash spaceship game, where the main control is via the microphone: shout louder to make your ship fly higher, to try to avoid the obstacles that appear on the screen. In this first part, we’ll focus on the design of the game, by drawing the main game objects and putting together the (mouse-controlled) menu and screen interface.


      Final Result Preview

      Here’s a look at the design of the game, which we’ll be putting together:


      Creating the Graphics

      Don’t like ads? Download the screencast, or subscribe to Activetuts+ screencasts via iTunes!

      In this section, we’ll draw our game’s main graphical objects: a spaceship, a laser beam, a diamond, a meteor, a planet, and a star – all in the pixel art style seen in the SWF above, and all using Flash Pro.


      Designing the Menus and Screens

      Don’t like ads? Download the screencast, or subscribe to Activetuts+ screencasts via iTunes!

      Here, we’ll create our menu interface, help screen, game over screen, and HUD.

      Links mentioned in the video:

      • Abstract Font
      • Installing Fonts in Windows
      • Installing Fonts for the Macintosh

      (Apologies for the weirdness that appears at around the 8:20 mark!)


      Coding the Menu Interface

      Don’t like ads? Download the screencast, or subscribe to Activetuts+ screencasts via iTunes!

      Now that we have all of the graphical elements, we’ll write the code to make our menus interactive, and handle the navigation between screens.

      More info on AS3 Events: Taking a Closer Look at the ActionScript 3.0 Event Framework.

      Check back in a couple of weeks for the second part of the series, where you’ll learn how to program the actual game mechanics – including the microphone-based interaction!


    6. Dru Kepple says:
      October 30, 2011 at 3:03 am

      Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Activetuts+. This week’s retro-Active tutorial, first published in October 2010, is an introduction to object-oriented programming in AS3. Don’t forget to check out the other AS3 101 tutorials!

      Object-Oriented Programming is a bit of a hot topic. More and more programming languages are cropping up that support it (Ruby, for example), and more and more languages that previously didn’t support Object-Oriented Programming (OOP for short) have been released with versions that do, like PHP and ActionScript. Some languages only work if you utilize the whole OOP thing to begin with, like Java and Objective-C.

      It’s actually been around for a long time, but came into the limelight in the 1990′s primarily due to its advantages with programming GUIs and the rise of C++ and Objective-C. Wikipedia has an interesting section on the history of OOP, which is a great place to start learning.


      Introduction

      If you’re new to Object-Oriented Programming, you have a bit of a learning curve ahead of you. However, embracing this technique isn’t just the “in” thing to do; it can decrease your development time while also decreasing the number of bugs in your code. It keeps you organized and prevents needless duplication. But perhaps more compelling than those lofty promises is the fact that Adobe has certainly put all of their weight behind the concept of OOP when it comes to ActionScript. ActionScript 1 was not Object-Oriented at all, and AS 2 was only Object-Oriented as a developer convenience. In contrast, AS 3 supports full-bore Object-Oriented features, and if anything, will only do more so in the years to come. So, if you like programming Flash and don’t want to get left behind, it would do you well to adopt OOP as a way of life.

      In this tutorial we will gradually introduce the some of the core concepts involved in Object-Oriented Programming. The final product coming out of Flash will be a bit lackluster, but this is just Part 1. You will see some practical explorations of OOP techniques, even if this tutorial isn’t really about building a specific project.

      You will, however, learn to make a reusable button which can be used in cases where a quick-and-dirty button is required, or even, with modification, as a key element to your next project.


      Step 1: The Big Question

      So what is this Object-Oriented Programming all about?

      First, let me explain what it is not. Object-Oriented Programming (or OOP for short) is not the only or even necessarily the best way to program in ActionScript 3. There seems to be a myth floating around ever since ActionScript 3 debuted that, in order to use AS3, you need to use OOP.

      While it’s true that AS3 is much better-suited to OOP than AS2 was, and that you are encouraged to move in that direction, it’s not true that you have to learn to write classes and instantiate objects in order to build real applications in Flash. If you’ve followed the AS3 101 series up until now, you may have noticed that every line of ActionScript 3 that we wrote in the process was in the script panel, not a class file. This should help illustrate that it’s perfectly acceptable to not utilize OOP as you work in Flash.

      That being said, ActionScript 3 really does shine when you start to embrace OOP. And not just AS3, but any language that supports OOP can be utilized more effectively when OOP techniques are engaged. This is because OOP is really just a technique – a big, complex technique with lots of options to consider, but still, just a technique – and not a mandate. And it is a great technique, one that you should start mastering. And that’s why you’re here, right?


      Step 2: Classes and Objects

      Object-Oriented Programming really boils down to two fundamental units: classes and objects. The two are intrinsically related: a class begets an object.

      Think of a class as a blueprint of a house, and an object as an actual house. The blueprint is more of a schematic for what the house will be like, not an actual house. When you write object-oriented code, you write the blueprints, the classes. Your code then creates the objects from these classes when it runs.

      A blueprint can also be re-used to create more than one house. In certain housing developments, you can see this in action; a cost-saving technique that pays the architect for one blueprint, but generates several houses from that blueprint. These houses are all unique entities, and what goes on in one house is isolated from what goes on in another house. However, they share a lot of features in common, such as the layout of the plumbing and the electrical wiring, or where the stairs are located. The houses may all have a front door at the same location, but one house might have a metal door painted red, and another house might have an unpainted wooden door with a knocker.

      A class, similarly, can create more than one object. These objects are all of the same type, but are all unique entities, like the houses. Again, they share many common features, but maintain separate identities. An object that loads data from an external source would have, as a common feature, the ability to load data and dispatch a complete event when it is ready. However, one instance of this loader would have a unique characteristic that loaded data from one URL, while another instance would load data from a different URL.

      The term instance is a word that describes an object. When an object is created from a class, it is said to be instantiated, and thus an instance of the class is born. The word object is typically synonymous with instance.

      If the terminology is troubling you, keep in mind the Library Symbol/Instance relationship that’s been around since Flash 1. A symbol is a single item in the library. An instance is a particular manifestation of that symbol. There can be more than one instance of a given symbol, and changes to a single symbol’s artwork will change all instance of that symbol. Yet each individual instance can maintain some unique properties, like position, rotation, color transformation, filters, and scale (to name but a few). This is directly analogous to class and objects (instances). In fact, it’s more than just analogous, as we’ll see in the next installment of this series.


      Step 3: Meet the Document Class

      Well, let’s jump in, shall we? I think the best way to get your feet wet in OOP AS3 is to become familiar with the document class. The document class is a class like any other in AS3, except that it has a very special relationship with a Flash file. It is the class that represents the SWF that you publish. The document class gets instantiated when the SWF starts up. This is analogous to the idea of a “main” function if you have a background in C, or the “main” method of the main class in Java.


      Step 4: Make a Project Folder

      Let’s make a document class, which will act as a basis for steps later in this tutorial. To do so, first create a folder on your filesystem that will house this particular project.

      Now make a Flash file (ActionScript 3.0), and save it to this folder. The name isn’t terribly important, but I’ll be naming mine MeetTheDocumentClass.fla


      Step 5: Document Class Text File

      Now you need to make a text file. You can use Flash CS3+ for this, or you can use Flex/Flash Builder, or you can use any other capable text editor, really. FlashDevelop is a great (and free) choice, if you’re on Windows. There are many choices in text editors, and there are no right answers (although, as I tell my students, there is one wrong answer. The text editor built into Flash CS3/4 is actually quite abysmal, and the sooner you embrace that fact the sooner you’ll enjoy coding in a real editor). Personally, I’m fond of TextMate, but keep in mind that what matters is the text within the file.

      All that being said, create a new file in the editor of your choosing (if you’re using Flash CS3+, go to the File menu, choose New, and then choose ActionScript File from the list. If you’re not, then I assume you’re familiar enough with your editor to be able to do this without direction from me).

      Save this file as DocumentClass.as in the same folder as your Flash file. This is important: the location and the name matter very much. The name doesn’t have to actually be “DocumentClass.as” but it’s what I’ll be using and to prevent confusion, I recommend you just do what I do. The name can be anything you want (more or less), but my point is that the name plays an important role in AS3 OOP, so be paying attention. Same deal with the location of the file; technically it can be anywhere but for convenience just follow my lead. More options will be revealed by the end of this series.

      To summarize, here’s my project folder as it stands:


      Step 6: Write the Document Class

      You are now faced with an empty file. If you’ve ever suffered writer’s block (I have, which regular readers might have guessed…), you can be comforted in the fact that the first thing you need to write is actually boilerplate. You’ll need to write the following:

      package {
      	import flash.display.MovieClip;
      	public class DocumentClass extends MovieClip {
      		public function DocumentClass() {
      
      		}
      	}
      }

      Wow, what is all that? Well, it’s a bit more than we need to explain right now. I’ll just point out a few places of interest.

      First, note that the package wraps up the entire thing. Don’t worry about what this is right now, just remember that it needs to be there, and the class needs to be contained within it.

      Second, we have an import statement. You may not have seen a line of code like this before; if you’ve stuck to coding in the Script panel up until now and haven’t made use of third-party ActionScript libraries, you’ve never needed to write an import statement. In OOP-world, each class needs to import the various classes it needs in order to do its thing. More on imports later.

      Third, the next “wrapper” is the class itself. You can see the keyword “class” in the third line. The anatomy of this line is more than we need right now, just note that the word following the word “class” is the name of the class. You may have noticed that the name I used is the same as the name of the file, sans extension. This is not a coincidence. For classes to work in AS3, the name of the class must match the name of the file. Don’t forget that.

      We’ll come back to the “extends MovieClip” bit in the next tutorial. For now, just know that this bit is required for a document class (you can also extend Sprite, a la “extends Sprite“, if you don’t need the timeline, and if you change the import line to “import flash.display.Sprite;“)

      Finally, we have the constructor.


      Step 7: The Constructor

      In the previous example, there is an (almost) normal function defined within the class. Almost normal, except for that “public” in front, and the missing datatype. Again, don’t worry about “public” for now, that’ll come in time. However, note the name of the function. That’s right, it’s the same as the file and as the class name. Hopefully you’re not overly paranoid, because this isn’t a conspiracy. It’s just the way things work. By naming the function with the same name as the class, we create a special function called the constructor. The constructor gets executed during instantiation. Hopefully you’re keeping up with the new terminology, because we just used two new terms in the same sentence. nerdCred++;

      An object will be able to have other functions in it, which you are welcome to define, but the constructor is the only function that gets called automatically during instantiation. The constructor, therefore, is where you would want to put code that should execute to get an object ready for initial use. For example, if you’re writing a class for an object whose job it will be to load some XML, parse it, then load thumbnail images based on that data, you might want to create a URLLoader, set up its event listener, and perhaps even start loading, all when you create the object. That logic could then go into the constructor.


      Step 8: Hello, World!

      As a practical example, we’ll do a little “Hello World” program. We won’t simply trace the words to the Output panel, no, that’s a little mamsy and definitely too pamsy. We’ll create a TextField, add it to the stage, and put some text into it. Expand your DocumentClass.as file to look like this (changes are in bold):

      package {
      	import flash.display. MovieClip;
      	import flash.text.TextField;
      	public class DocumentClass extends MovieClip {
      		public function DocumentClass() {
      			var tf:TextField = new TextField();
      			addChild(tf);
      			tf.text = "Hello World";
      		}
      	}
      }

      Note that in addition to the three lines added to the constructor, there is now an extra import line in anticipation of the TextField.

      We could obviously take it further, positioning and styling the text, and you’re welcome to do that on your own, but for now this serves the purpose. This code creates a text box with the words “Hello Word” in it, and displays it on stage. Except for one thing: The Flash file doesn’t know that it’s supposed to have a document class. If you try to run your Flash file now, you’ll get an empty window. Let’s fix that next.


      Step 9: Assigning a Document Class

      As mentioned at the end of the last step, the Flash file (MeetTheDocumentClass.fla in my case) doesn’t yet know that it has a specific document class associated with it. Technically, it does have a document class, only the document class is just plain old MovieClip, so it’s not going to do much.

      It’s good to know that, but what we really want right now is to let this Flash file know where the DocumentClass.as file we just wrote is. To do that, first make sure nothing is selected (you can click anywhere on the stage where there isn’t a visual asset, or you can choose Edit > Deselect All, or press Command-Shift-A or Control-Shift-A).

      Next, open up the Properties panel. If you’ve successfully deselected everything, the Properties panel should say “Document” at the top

      Now, where it says “Class:” under the “Publish” section of the Properties panel, type in “DocumentClass” (or whatever name you used for your version of the class, if you insist on forging your own path). Note that this is without the “.as” – it’s just the class name, not the file name.

      If you’ve followed along with the directions so far, you should be able to press Return/Enter and all will be well. If somehow you got the class name wrong, or didn’t save the files in the same folder, you won’t get an error when you publish. You should have gotten this error when typing in the class name:

      If you ignored it, or didn’t see it because you had previously checked that “Don’t show again” option, then the SWF will publish without error, but you’ll get a plain white screen. If this happens, double-check the class name that you’ve entered into the Properties panel.

      Once the document class is properly hooked up, you can verify that by clicking the little pencil icon next to the document class text field in the property panel. This will open up the file in Flash CS3+ for editing. Not that I’d recommend keeping it there, but verifying that you’ve got the right file can be a helpful troubleshooting tip.

      Once everything checks out, go ahead and test your movie. You should see a plain window with the words Hello World in it:


      Step 10: Properties

      At this point, you are set up to put your application logic into the document class and start making a more interesting SWF. However, you will likely need more than just the constructor function to make said interesting SWF. Usually you need to create other objects, stick them in variables, use functions to listen for events and have these things interact in a meaningful way. Of course, in OOP you have such things as variables and functions, only they properly go by different names: properties and methods (we’ll tackle methods in the next step).

      Properties are, at their essence, simply variables. There is a slight difference between a property and a variable, though. It’s subtle, and we’ll have to illustrate it with an example, but we have some ground to cover before we get there.

      First, a property is written identically to a variable, with two caveats. Here’s an example:

      private var tf:TextField;

      The first caveat should be obvious: there’s a big old “private” in front of the var keyword. “private” is similar to the “public” I asked you to ignore a few steps ago. I’m going to ask you to continue ignoring it. We won’t get to those until the next tutorial.

      The second caveat won’t be obvious, because the snippet is out of context. This is more appropriate:

      public class DocumentClass extends MovieClip {
      	private var tf:TextField;
      	public function DocumentClass() {
      		// ...
      	}
      }

      Notice the position of the property in relation to the structure of the class. The constructor function and the property are both at the same “level” and “belong” directly to the class (they are both nested into the class directly). This characteristic makes the variable a property (despite being declared by using the var keyword).

      In contrast, the variable named “foo” in the snippet below is merely a variable:

      public class DocumentClass extends MovieClip {
      	private var tf:TextField;
      	public function DocumentClass() {
      		var foo:String = "bar";
      	}
      }

      Why? Because it’s “owned” by the function, not by the class. When an variable or function gets declared within the confines of curly braces (which is technically every time), it exists for only as long the thing represented by the curly braces exists. So – and here is the subtle distinction – the property “tf” exists so long as the object “DocumentClass” is around, and “belongs” to a DocumentClass object, but the variable “foo” exists only as long as the function containing it runs, and ceases to exist once the function finishes executing.

      There are, naturally, exceptions to that “curly brace” rule. Loops, for instance, don’t affect the scope of a variable declared within, so take that rule with a grain of salt. You won’t find it mentioned in programming books.

      You might be asking,

      but wait, doesn’t the function exist in the object at the same level as the property? Doesn’t it exist just as permanently as the property, and therefore wouldn’t the variable inside the function exist as well?

      You have a point, however, the distinction is that the potential to run a function exists within the object, while the actually running function exists only at the moment it gets called. The variable gets created during the run, and therefore ceases to exist once the run is done. I did say the distinction was subtle. But it is important, as we’ll see soon.


      Step 11: Methods

      Before we get to that, I need to extrapolate what we just learned to functions and methods. Basically, methods are just functions that belong to an object, like the constructor function, only they aren’t elevated to any special status. Here, we added another method:

      public class DocumentClass extends MovieClip {
      	public function DocumentClass() {
      		// ...
      	}
      	public function onButtonClick(e:MouseEvent):void {
      		trace("click");
      	}
      }

      Now, onButtonClick() is a pretty familiar function, except for that pesky public at the beginning. Once again, put that off. Otherwise, you should recognize this as a regular function. The only difference is that the same concept of “ownership” applies to methods as it does to properties, versus regular variables and functions. Finally, let’s explore this with a working example.

      In your DocumentClass, we’ll flesh out the snippets we’ve seen. Make your class look like this:

      package {
      	import flash.display. MovieClip;
      	import flash.text.TextField;
      	import flash.events.MouseEvent;
      
      	public class DocumentClass extends MovieClip {
      
      		private var tf:TextField;
      
      		public function DocumentClass() {
      			tf = new TextField();
      			addChild(tf);
      			tf.text = "Hello World";
      
      			stage.addEventListener(MouseEvent.CLICK, onButtonClick);
      		}
      
      		private function onButtonClick(e:MouseEvent):void {
      			tf.text = "Hey, you clicked!";
      		}
      
      	}
      }

      Notice the changes and additions: We’ve added a property called “tf“. In the constructor, we still create a TextField, but instead of creating the variable in the constructor like we did before, we simply use “tf” – the property. Our TextField is now stored in the property instead of a variable.

      We also added a click listener to the stage (no, the stage isn’t really a button, but in this case it saves us from having to create a MovieClip to act as a button). And that listener is a method called onButtonClick(), and other than being a method instead of a function, it’s identical to any other event listener.

      What this method does is significant: it replaces the text in the textfield with another string. This can only happen if tf retains it value over time. If the movie worked, then we know tf still references the TextField created in the constructor, even though several seconds probably passed between the creation of the TextField and the re-population of it.


      Step 12: Exploring Scope

      Now, if you remove the property and return the TextField creation to be a variable, you actually won’t even be able to test it, as the compiler will give you an error saying it couldn’t find a property called tf. That is, this code:

      package {
      	import flash.display.MovieClip;
      	import flash.text.TextField;
      	import flash.events.MouseEvent;
      
      	public class DocumentClass extends MovieClip {
      
      		//private var tf:TextField;				//we have removed this line!
      
      		public function DocumentClass() {
      			var tf:TextField = new TextField();
      			addChild(tf);
      			tf.text = "Hello World";
      
      			stage.addEventListener(MouseEvent.CLICK, onButtonClick);
      		}
      
      		private function onButtonClick(e:MouseEvent):void {
      			tf.text = "Hey, you clicked!";
      		}
      
      	}
      }

      Produces this error:

      What happened? Well, in ActionScript, a variable created in one function exists for the duration of the function (which is over a few lines later), and is gone once the second function runs. So if we create a variable tf in the constructor function and try to use it in the onButtonClick() function, we have a problem. tf no longer exists when onButtonClick() runs.

      However, properties persist throughout the life of an object, so if we store the TextField in a property instead of a variable, we have access to that TextField in other functions, at pretty much any time.

      In this context, variables are sometimes referred to as local variables because they are local to the function.

      And this is the subtle point that started this marathon step. Hopefully it sunk in, because I find this point to be the cause of many “gotchas” for beginning OOP students.

      Keep in mind that there is more going on with properties, methods, persistence and references. I feel I’ve belabored the point enough, and won’t be doing you any favors by dissecting the finer points of references and delaying other essential knowledge.


      Step 13: About That Button…

      OK, in the last step I skipped the creation of a button and just used the stage as a clickable area. We’ll change that, but not because clicking on the stage is wrong (it isn’t, in certain circumstances), but because we can create a Button class to further explore OOP.

      Our goal will be to create a Button class, from which we can create multiple Button objects. Each Button object will have some core functionality that is universal across buttons:

      • Hover and normal states
      • The cursor turns to a hand icon
      • A graphic background with a text label

      At the same time, there will need to be things unique to each button:

      • The text of the label
      • The click action
      • The position of the button

      We’ll look at what it takes to create a second class involved in your project and incorporate these requirements as we go.


      Step 14: Create a Button Class

      We’ll create a class that, when instantiated, draws a rectangle, puts text into a label, handles roll overs and outs, and can respond to clicks. Start by creating a new text file in your text editor. Save it as “Button101.as” in the same folder as your Flash file. This is important. Don’t mess it up! I’m serious.


      Step 15: Write the Boilerplate

      In your new file, start stubbing in the class boilerplate:

      package {
      	import flash.display.Shape;
      	import flash.display.Sprite;
      	import flash.events.MouseEvent;
      	import flash.text.TextField;
      	import flash.text.TextFormat;
      
      	public class Button101 extends Sprite {
      
      		public function Button101() {
      
      		}
      
      	}
      }

      Step 16: Add Some Properties

      Next we’ll add some properties to hold the background shape and the label:

      	// ...
      	public class Button101 extends Sprite {
      
      		private var bgd:Shape;
      		private var labelField:TextField;
      
      		public function Button101() {
      		// ...

      The bgd property will hold a reference to a Shape which we’ll draw programmatically to be a filled rectangle, and add as a child of the Sprite.

      The labelField property will hold a reference to a TextField which we’ll create with code, as well, and use to display a text label on top of the background.


      Step 17: Add the Logic

      And now we’ll pop in the actual logic. For the constructor:

      public function Button101() {
      	bgd = new Shape();
      	bgd.graphics.beginFill(0x999999, 1);
      	bgd.graphics.drawRect(0, 0, 200, 50);
      	addChild(bgd);
      
      	labelField = new TextField();
      	labelField.width = 200;
      	labelField.height = 30;
      	labelField.y = 15;
      	var format:TextFormat = new TextFormat();
      	format.align = "center";
      	format.size = 14;
      	format.font = "Verdana";
      	labelField.defaultTextFormat = format;
      	addChild(labelField);
      
      	addEventListener(MouseEvent.ROLL_OVER, onOver);
      	addEventListener(MouseEvent.ROLL_OUT, onOut);
      
      	mouseChildren = false;
      	buttonMode = true;
      }

      That’s a lot of code, but it’s nothing surprising for the most part. We’re creating a new Shape and drawing a rectangle to into it. Then we create a TextField, set it up with some basic formatting. Next up, we add some rollover and rollout events (the listeners for which we’ll add in the next step). Finally we set some properties to make the object behave more like a button.

      The only part that might be causing confusion is the call to addChild(), and where the mouseChildren and buttonMode properties came from. I’ll ask you to yet again wait for a more complete answer, as that will come in the next installment, but it has a lot to do with the “extends Sprite” we wrote earlier.


      Step 18: Add Some Methods

      Now, we can continue working on our class. After the constructor, create a function called setLabel:

      public function setLabel(label:String):void {
      	labelField.text = label;
      }

      And finally, create the two event listeners:

      private function onOver(e:MouseEvent):void {
      	bgd.alpha = 0.8;
      }
      private function onOut(e:MouseEvent):void {
      	bgd.alpha = 1;
      }

      The final code should look like this:

      package {
      	import flash.display.Shape;
      	import flash.display.Sprite;
      	import flash.events.MouseEvent;
      	import flash.text.TextField;
      	import flash.text.TextFormat;
      
      	public class Button101 extends Sprite {
      
      		private var bgd:Shape;
      		private var labelField:TextField;
      
      		public function Button101() {
      			bgd = new Shape();
      			bgd.graphics.beginFill(0x999999, 1);
      			bgd.graphics.drawRect(0, 0, 200, 50);
      			addChild(bgd);
      
      			labelField = new TextField();
      			labelField.width = 200;
      			labelField.height = 30;
      			labelField.y = 15;
      			var format:TextFormat = new TextFormat();
      			format.align = "center";
      			format.size = 14;
      			format.font = "Verdana";
      			labelField.defaultTextFormat = format;
      			addChild(labelField);
      
      			addEventListener(MouseEvent.ROLL_OVER, onOver);
      			addEventListener(MouseEvent.ROLL_OUT, onOut);
      
      			mouseChildren = false;
      			buttonMode = true;
      		}
      		public function setLabel(label:String):void {
      			labelField.text = label;
      		}
      		private function onOver(e:MouseEvent):void {
      			bgd.alpha = 0.8;
      		}
      		private function onOut(e:MouseEvent):void {
      			bgd.alpha = 1;
      		}
      	}
      }

      One thing that is very important is to make sure your methods are declared at the class level. This is another rookie mistake: sometimes people don’t pay attention to where their curly braces start and stop, and end up nesting what should be a method (that is, declared at the “root level” of the class) inside another method. This effectively turns a method (which should persist for the life of the object) into a local function (which only persists for the life of the running method in which it was declared).

      Hopefully compiler errors will notify you of such a mistake, but the error itself might be cryptic. It would say “1114: The public/private/protected/internal attribute can only be used inside a package.” If you get that, check the nesting level of your methods. Another thing to help prevent these errors is to pay careful attention to whitespace and indentation. A “real” text editor can help you with this, but just make sure all of your method declarations are at the same indentation level (typically two tab stops).

      What we have now is a class ready roll, which, when instantiated, will create a customized display object that has a gray rectangle with a label. The label can be set using a method and the button will automatically respond to mouse roll overs and roll outs. All we need to do is actually instantiate it.


      Step 19: Create a Button

      Step back to your document class, and we’ll add a button to the movie. This will be pretty easy compared to the previous step.

      First, we’ll add a new property to hold the button. Stick it below the line with your existing property.

      private var tf:TextField;
      private var button:Button101;

      It’s not required to put all properties together, but for the organization of your file, it’s helpful to keep things in consistent places. Personally, I always list properties at the very top of the class, before the constructor. This way, I always know where to go to find a property declaration. Where they go in the file isn’t as important as coming up with your own standard and sticking to it.

      Moving on, we can now instantiate the Button101 in the document class’s constructor:

      public function DocumentClass() {
      	tf = new TextField();
      	addChild(tf);
      	tf.text = "Hello World";
      
      	button = new Button101();
      	button.x = 10;
      	button.y = 200;
      	button.setLabel("Button 1");
      	addChild(button);
      	button.addEventListener(MouseEvent.CLICK, onButtonClick);
      
      	//stage.addEventListener(MouseEvent.CLICK, onButtonClick);		//we have removed this line
      }

      Note that we modified the line that set up a listener for clicks on the stage. We’re shifting that interactivity to the button.

      Now, you may be wondering how it is that we’re treating this Button101 object as if it were a Sprite or MovieClip (see the AS3 101 tutorial on the Display List if you weren’t wondering that). This has everything to do with that extends business I’ve asked to gloss over for now. I promise, we’ll get to it soon (in the next part of this series).

      At this point, we should be able to test it out. It should work similarly as before, except that you’ll see the button appear, and instead of clicking anywhere on the stage, you need to click on the button.

      For reference, here is the complete document class at this point, with additions and changes in bold:

      package {
      	import flash.display.MovieClip;
      	import flash.text.TextField;
      	import flash.events.MouseEvent;
      
      	public class DocumentClass extends MovieClip {
      
      		private var tf:TextField;
      		private var button:Button101;
      
      		public function DocumentClass() {
      			tf = new TextField();
      			addChild(tf);
      			tf.text = "Hello World";
      
      			button = new Button101();
      			button.x = 10;
      			button.y = 200;
      			button.setLabel("Button 1");
      			addChild(button);
      			button.addEventListener(MouseEvent.CLICK, onButtonClick);
      
      		}
      
      		private function onButtonClick(e:MouseEvent):void {
      			tf.text = "Hey, you clicked!";
      		}
      	}
      }

      So, what did that accomplish us? The big thing is that we got a pretty nifty button that we could use with very little effort (not counting the effort put into writing the Button101 class in the first place).

      To accomplish the same functionality without classes and OOP, we’d have to write considerably more code in our main script. To illustrate this further, let’s create a second button.


      Step 20: Create Another Button

      OK, now let’s let OOP really shine. We’ll create a second instance of the button, merely by adding a few more lines of code to the document class.

      The changes involved will be extremely similar to the previous step. First, add a property:

      private var tf:TextField;
      private var button:Button101;
      private var button2:Button101;

      Then set it up in the constructor:

      public function DocumentClass() {
      	tf = new TextField();
      	addChild(tf);
      	tf.text = "Hello World";
      
      	button = new Button101();
      	button.x = 10;
      	button.y = 200;
      	button.setLabel("Button 1");
      	addChild(button);
      	button.addEventListener(MouseEvent.CLICK, onButtonClick);
      
      	button2 = new Button101();
      	button2.x = 220;
      	button2.y = 200;
      	button2.setLabel("Button 2");
      	addChild(button2);
      	button2.addEventListener(MouseEvent.CLICK, onButtonClick);
      }

      You can, in fact, just copy and paste the first button’s lines of code and just change button to button2, and also the x position of the second button (otherwise button2 will sit right on top of button1).

      We’ve doubled the complexity of our application in just a few short lines of code, thanks to the reusability of the Button101 class. We have the class, which, if you remember, is the blueprint from which the actual instances are created. We now have two distinct buttons, yet they share that common “heritage.” Even though there are many characteristics that are the same between these two buttons, like the rollover effect and the size, there are still aspects of individuality, namely the position.

      Hopefully this starts to illustrate the usefulness and power of Object-Oriented Programming, in which we facilitate the reuse of code very easily.

      Go ahead and test it out; you should have two buttons that do the exact same thing.

      Naturally, you’ll probably want to tweak the Button101 class so that it looks fancier, or has a slicker roll over effect, or what-have-you. By all means, take the Button101 class and make it more useful for your own needs. Our goal here was to demonstrate how a button class can be made, not to make the ultimate button class.


      Step 21: Summary

      There were two main themes in today’s tutorial. To recapitulate, these are: the idea of blueprints (classes) and houses (instances); and the notion of scope.

      Classes are the code files that we write, as well as the concept of an entity that serves as the blueprint for the objects that will get created from them. One class can beget any number of instances. Objects are the instances of classes, and tend to be the things that, when put together, comprise our application. Instances of the same type share functionality in common, but retain unique identities and can possibly contain variances in the data contained within.

      Scope is an important concept through all of programming, and no less so in Object-Oriented Programming. Scope determines the “life span” of a variable or function, and also a “place to live.” A variable at the object scope has a different life span than a variable at the method scope.


      Until Next Time

      This is, however, just scratching the surface of the tip of the iceberg. It’s all we have time for right now, but in the soon-to-be-published Part 2 of the AS3101 OOP series, we’ll dive a little deeper and finally get an explanation on all of things I was asking to put aside, like extends and public. Things will begin to make more sense. But when it comes to a complicated topic like Object-Oriented Programming, it’s best to take things slow.


    7. Activetuts+ Editor says:
      October 30, 2011 at 3:24 am

      Fans of the Activetuts+ Facebook page can now access a new bonus tutorial – in this month’s Facebook Fan Bonus, you’ll learn how to remove the background color from a sprite sheet using AS3, and blit the result to a bitmap canvas.

      Final Result Preview

      Here’s a look at the kind of effect you’ll be working towards in this tutorial:

      Blitting in Flash: removing background

      Download This Fan Bonus Now!

      All you have to do is Like us…


      Not On Facebook?

      Don’t worry, the tutorial will be posted on Activetuts+ in a month’s time!


    8. Anthony Lombardo says:
      October 30, 2011 at 3:46 am

      I can’t tell you how many articles and tutorials I have come across on how to use symbols in Flash that immediately dismiss the graphic symbol as having no practical purpose, relegating it as just a step above grouping items. This article will attempt to dispel this myth by showing that the graphic symbol actually has some pretty cool and convenient features and knowing how and when to utilize them is a nice tool to have when you’re creating animations in Flash.

      I first began using Flash with version MX. And through all the enhancements and added features in every release, one thing that has remained constant is the graphic symbol. But what has also remained constant, surprisingly enough, is how many Flash users don’t know what the graphic symbol actually does. Somewhere along the line, this symbol has received a bad rap as being totally useless. If you’ve ever wondered what exactly the purpose of the graphic symbol is and why the heck Adobe continues to keep it in Flash, this article is for you.


      The Basics

      We’ll start off with the most known and basic of things you can do with the graphic symbol that are inherent to all Flash symbols. Just like movie clips and buttons, a graphic symbol can have color effects applied to it like alpha and tint, get broken apart into its comprising elements via CMD/CTRL-B, and of course have its properties motion-tweened.

      I know that’s far from a revelation, but I mention these attributes because I think it’s important to keep in mind that a graphic symbol at its core is just like any other Flash symbol but with its own unique set of features.


      Animation Misconceptions

      One of the biggest misconceptions about the graphic symbol is that it cannot contain animation – or that, if it does, Flash will just ignore it at runtime. Not only is that completely untrue, but in actuality graphic symbols are very powerful and flexible when it comes to creating timeline animation.

      Just like a movie clip, a graphic symbol can contain nested animations on top of nested animations of other graphic symbols and movie clips. It is the symbol of choice used by most professional Flash animators, especially when it comes to creating complex timeline animation with lots of vector assets. Let’s see why.


      Animation and the Properties Panel

      The true power of the graphic symbol lies within the Properties panel. This is where you can control the animation inside a graphic symbol, which is one of the hidden, unknown jewels in Flash. The approach that I am about to detail is really an animation technique referred to by many as the Chris Georgenes method.

      If you’re not familiar with Chris Georgenes, he is a well-respected Flash animator who innovated this approach to animating with graphic symbols and I was lucky enough to take a character animation course he was the instructor of. I highly recommend you read up on many of his Flash animation techniques and tricks.

      graphic symbol properties panel

      That’s enough background information so let’s get cracking with the good stuff. When you create a graphic symbol (Modify > Convert to Symbol), you have three options for handling how the symbol will play back its animation; to see them, with the symbol selected, go over to the Properties panel and look under the Looping category.

      When you click on the Options drop-down, you will be presented with three options to choose from: Loop, Play Once and Single Frame. Also below the drop-down, you’ll see a small input field labeled ‘First’. Before we get to that, let’s understand the three looping options – but keep that little input field in mind as we go through each option as it will all tie together.

      Note: If you don’t see the Options field, just click on the arrow to the left of Looping to reveal the drop-down.

      looping options of a graphic symbol

      Loop: Loop is the default setting for every new graphic symbol that is created. Basically this option will constantly loop any and all animation that is nested inside it. So in other words, the symbol’s timeline will play through its duration and then start over again, just like a movie clip.

      Play Once: This setting is pretty self-explanatory as well. It will play the symbol’s timeline once through and then stop. Think of it as the graphic symbol’s way of adding a stop() action on the final frame of its timeline.

      Single Frame: Single frame allows you to display only a certain frame within the symbol’s timeline. Whereas the symbol’s timeline is actually playing when it’s set to Loop or Play Once, the Single Frame option just jumps the timeline to a specific frame within the symbol and stays there. I like to think of it as the graphic symbol’s version of using gotoAndStop(frame). So how is this done? If you’re recalling that tiny input field labeled ‘First’ that I told you to keep in mind a few paragraphs back, you’re heading in the right direction.


      First Frame Input Field

      Though very ambiguous and easily unnoticed, this input field is where all the fun happens for controlling the timeline of a given symbol. This is one of my favorite features of the graphic symbol because you can target a specific frame on the symbol’s timeline by typing a frame number in the input field.

      When a graphic symbol is first created, the first frame input field is set to 1 by default – as denoted by the 1 inside the field. This means frame 1 on the symbol’s timeline will be the first frame the animation will begin on. That’s where the label First comes in; think of it as the first frame you want to display or the first frame you want to play from. So if you have a symbol set to Single Frame and type 6 in the first frame field, it will display whatever is on frame 6. If the supplied frame number is blank or if the symbol doesn’t even have that many frames in its timeline, then nothing will display.

      You can also use the first frame field with the Loop and Play Once options. If you set a symbol to Play Once and type in a certain frame number in the input field it will start from that frame. The same applies if you set it to loop. The symbol would begin to loop starting with the frame number you supplied in the input field. Keep in mind though that if you typed in 6, it will start to loop from frame 6 but once it reaches the end of the timeline it will continue to frame 1 in its usual manner.


      But It Still Isn’t Animating

      Now, if you took it upon yourself to try any of this out within Flash, you’re probably wondering why your animation isn’t playing in the manner I’ve been describing. Open up the file named looping_basics.fla in the source package. Double-click the graphic symbol that is on the stage to check out the animation that’s inside of it. You’ll see two layers spanning 239 frames on the timeline, with a new keyframe on both layers every 30 frames (i.e. every second). So if you scrub the play head, you’ll see we basically have an 8-second animation in which on every second the number in the center increments by one and the circular background changes color. By all means nothing fancy.

      After you have the gist of the animation, return to the main timeline, click on the graphic symbol and check the properties panel to make sure the symbol is set to Loop and 1 in the first frame field. Now test out the movie.

      What’s going on here? Our symbol isn’t animating even though we have it set to Loop. Right about now you might want to say you told me so about Flash just ignoring a graphic symbol’s animation. Before you do so, let’s get to the bottom of this.


      It’s All About the Timeline

      This is the common pitfall most people experience when they create an animation inside a graphic symbol. And it’s probably the reason why the symbol is thought of as useless. You create an animation on the graphic’s timeline then position the symbol on the main timeline just like you would with a movie clip. But when you go to export your movie, the symbol just sits there static on the stage. This is where the misconception lies: we are expecting the graphic symbol to behave just like a movie clip.

      The trick to the graphic symbol is that it is timeline driven – meaning its own timeline coincides with the timeline the symbol is placed on. So if it’s placed on the stage, it needs just as many frames on the main timeline as it has on its own timeline in order to play the animation in its entirety. In other words, whereas a movie clip will play independently of the timeline, a graphic symbol will be played only as far as its parent’s timeline will allow it.

      In a nutshell, a graphic symbol needs frames.

      Give It Some Frames

      If you go back to our example in Flash, the reason our graphic symbol isn’t playing is because there is only one frame on the main timeline. So let’s add some frames. Just for demonstration purposes, go to frame 30 and add frames. Now go to frame 60 and do the same. Do you see what’s happening here? Then go to frame 90. Very cool, huh? Keep adding as many frames as you want and test the movie. You should see the animation begin to play, depending on how many frames you have on your main timeline. Remember our symbol has 239 frames on its timeline, so in order to see the entire animation, there needs to be the same amount on the main timeline.

      Note: Keep in mind this principal we’ve been discussing doesn’t just apply for the main timeline. The timeline that the graphic symbol is actually placed on will dictate its playback. So for example, if your graphic is inside a movie clip, you need frames on the movie clip’s timeline to render the graphic’s animation. Try it out yourself.

      Automatic Playback

      As you probably noticed from the example file, you can actually see the graphic symbol animate as you scrub the timeline from the stage. Yes, very cool. This brings us to another unique feature that differentiates a graphic symbol from a movie clip and you’ll probably find it to be its most useful. Since the graphic symbol is timeline driven, a benefit of that is having your animation automatically update within Flash. As long as you have ample amount of frames on the timeline where your symbol is located, you can always see your graphic symbol animate live.


      Case Study: Facial Expressions

      Let’s explore these techniques a little more to give you another look on how you can incorporate a graphic symbol into a project. Open up the file named facial_expressions.fla that’s in the source package. You will be greeted with a nice smiling face on the stage that was part of an illustration for an animated character I created a few years back.

      character creation graphic symbols

      If you click on the symbol, you’ll see it’s a graphic with a library name of expressions, and that it is set to Single Frame 1. Go inside the symbol and you’ll see three layers: eyes, mouth and head, each of which contain graphic symbols. The head layer contains all the other elements that comprise this little guy’s smiling face, and if you unlock the layer and double-click the symbol you’ll see are all graphic symbols as well. For our purposes, though, we are going to be focusing on the graphic symbols for the mouth and eyes. Go inside the mouth symbol and you’ll see it contains various different mouth shapes at various points on the timeline, and the same goes for the eyes. Let me point out that the left and right eyes are instances of the same exact symbol.

      Go inside both the mouth and eye symbols and take a look around. You’ll notice that I have a frame labels layer corresponding to each facial expression and that I chose to put each of them on a keyframe that are 10 frames apart. There’s no reason for having the timeline set up like this other than it’s my own way of trying to keep it clean and organized. I probably wouldn’t use the frame labels in ActionScript (though I could if I converted my graphic to a movie clip) but having a label for every new expression lets me know at a glance where a certain expression is on the timeline. And besides giving me some space so the labels can actually be read, the 10 frame gap between each keyframe simply makes it easy for me to remember where a new mouth or eye expression occurs on the timeline.

      Now jump back one level to the expressions graphic where we just have our three layers of the mouth, eyes and head, and start using these symbols to create some animation. There’s a lot of different ways you can set things up here, depending on what you want to do, but let’s just keep it simple and make our guy blink.

      Insert a new frame on all three layers. Check that both the mouth and eye symbols are set to Single Frame 1 and then insert a new keyframe on the eyes layer and set both eyes to Single Frame 10.

      Go back to the stage, give yourself about 45 or so frames, make a new keyframe on frame 30 and change the 1 in the first frame field to a 2. Remember your keyframes should be set to Single Frame. Now test it out and your guy should be blinking. I know that’s nothing fancy but the point here isn’t so much about what can be created with the graphic symbol, but rather about the convenience and flexibility it provides you. We’ll get to more on this in a bit but hopefully this example has your gears turning.


      When Would You Use a Graphic Over a Movie Clip?

      Personally, any time I am working on a project that calls for creating numerous timeline animations, I consider using the graphic symbol. Any Flash user who has ever spent countless hours tinkering with numerous movie clips that all contain their own animations has probably run into a situation where they wish they could pinpoint where something was occurring on a certain movie clip’s timeline from another spot in the movie. From my own experience, there have been many times in which I have an animation inside one movie clip and want to use it within another clip but needed to know when a certain point in my animation was reached so I can have something else happen. I would have to resort to a tedious process of jumping back and forth between movie clips and counting frames so I could figure out where to place it on that second clip’s timeline. Using my animation as a graphic would allow me to see everything without having to export my entire movie.

      properties panel movie clip graphic

      Tip: Even when I am using a movie clip for a timeline animation, I sometimes temporarily set it as a graphic in the properties panel just so I can view the animation playback while other objects are animating around it. I can then get a better sense of the overall timing and do some tweaks; when I’m done, I set it back to act as a movie clip.

      To do this, just click on your symbol, go to the top of the Properties panel and click on the drop down. This is where you can change your symbol’s behavior on the fly, toggling it between a movie clip and a graphic. This is very useful when you want to use the same symbol as both a movie clip and a graphic throughout your project. Note: This technique will not work if you try to change between a movie clip and a button or between a graphic and a button.


      Practical Uses

      Here are some other practical uses of when you might want to consider using a graphic symbol:

      Animated Presentations: Presentations are the projects where I find myself using graphic symbols the most. Most flash presentations tend to be heavy on animation and syncing visuals to a script. The graphic symbol lends itself well to projects like these that are in linear in nature and have limited user interaction.

      Syncing audio with an animation: This is an extension of the previous point. Anytime you want pinpoint synchronization between a sound and an animation, running your audio alongside your animated graphic symbol will give you that type of control. Many voiceover presentations that I have been involved in have employed animated graphic symbols.

      flash presentation design

      Character animation: Anytime you are going to create intricate animations with vector artwork that has lots of layers, the graphic symbol is the way to go. Many cartoon animations have been made with flash and I would bet anything that if you were to examine the FLA they were made in, the library would be packed to the brim with graphic symbols. Just like facial expressions, creating sequences for walk cycles and lip syncing sequences are ideal jobs for using the graphic symbol.

      Creating multiple variations of a symbol: We already touched on this notion throughout the article but I think it’s worth further explanation. If you recall in our facial expressions example from earlier, the mouth and eye graphic were used to house all our different mouth and eye shapes for easy animating. Well, sometimes I use the graphic symbol to just hold a bunch of similar movie clips that are just slightly different, or what I like to refer to as poses.

      In some cases, I’m not looking to sequence these poses for an animation like in the facial expressions example but to act as a holder of my elements, which will simplify the process of accessing them later on. I find it much easier to refer to each pose if they are all on their own frame inside a graphic instead of having to sift through one long movie clip timeline with numerous layers. All I have to do is target the frame number of the one I want inside the first frame field like we did in the facial expressions example. And if I need to control any of my poses with actionscript, I just turn my graphic into a movie clip.


      Limitations

      The graphic symbol is not without its limitations and drawbacks. While the graphic symbol can be very useful in certain situations, by no means am I advocating it as a replacement to the movie clip. In most situations, a movie clip will be your symbol of choice and will do just fine even for creating most timeline animations. Here are some things to keep in mind when using the graphic symbol:

      Can’t be controlled by ActionScript: This is an obvious point but it’s a biggie. Unlike a movie clip, you can’t assign a graphic symbol an instance name and any script you place on its timeline will be ignored. Keep in mind though you can still use a movie clip that has ActionScript on its timeline as a graphic and it will play back normally.

      Can’t apply filters: For some reason, Flash doesn’t allow you to apply any of the filters or blending modes to a graphic symbol. To get around this, you can simply put your graphic symbol inside a movie clip and then apply the filter to the clip.

      classic tweening graphic symbol

      Classic tweening graphic symbols with nested animation can be problematic: This is one I learned the hard way and it’s an issue that can happen quite easily without you even realizing. Let’s say you have a graphic symbol with a pretty involved animation inside it, and you want to tween the symbol on the main timeline from left to right across the stage as your nested animation plays. You create your first keyframe on the timeline and you set it to Play Once starting on frame 1 in the Properties panel. When you create your second keyframe, Flash will automatically set it to Play Once and update the first frame field accordingly with the correct frame number in sequence.

      So using the above example if your first keyframe is on frame 10 on the main timeline and your next keyframe is on frame 40, the graphic symbol on this frame would be set to Play Once with the first frame field already set to 30. Get it? It inserted the frame number you left off from? Now if you were to add or delete frames within the tween, the symbol on that second keyframe will still have 30 set as its first frame to play from thus completely throwing off the order of your nested animation. It’s just something to keep in mind; when you initially create keyframes, the first frame number will update correctly, but once you start moving keyframes up and down the timeline, things will get thrown off.

      Note: This only applies to classic tweens. If you are using the method for creating motion tweens that was introduced in Flash CS4, this won’t be an issue.


      Conclusion

      Hopefully after reading this article you have a different opinion of the graphic symbol. Though it may not be something you can see yourself using all that frequently, the thing to keep in mind with the graphic symbol is that it’s about knowing when to use it. As a developer, I don’t use it all that often but I know I have it as an option if the situation calls for it. Like with most things in Flash, it comes down to a matter of personal preference. If you pick and choose your spots wisely, using the graphic symbol can be convenient and a time-saver.

      I know a lot was covered in this article, but the best way to really understand what the graphic symbol can do is to spend some time playing around with it in Flash. Change its looping options, target different frame numbers to start your animation on and toggle back and forth between using it as a graphic and a movie clip to really see what works and what doesn’t. So maybe the next time you hit F8 and you are planning on creating a timeline animation, remember the graphic symbol might not be that useless after all.


    9. Joseph Clover says:
      October 30, 2011 at 4:12 am

      In this Quick Tip, you’ll learn how to construct a reference to a class from a String, and then create an instance of that class, using a built-in AS3 function called getDefinitionByName(). You’ll also learn the best methods for using this in different situations.


      Why Is getDefinitionByName() Useful?

      getDefinitionByName() is very useful if you need to make new instances of classes by using a String. For example, if you had seven different tiles – each represented by a class called Tile1, Tile2, etc – and you needed to create an instance of each, you would have to write the following code:

      
      
      private function createTiles():void
      {
      	var tile1:Tile1 = new Tile1();
      	var tile2:Tile2 = new Tile2();
      	var tile3:Tile3 = new Tile3();
      	var tile4:Tile4 = new Tile4();
      	var tile5:Tile5 = new Tile5();
      	var tile6:Tile6 = new Tile6();
      	var tile7:Tile7 = new Tile7();
      	stage.addChild( tile1 );
      	stage.addChild( tile2 );
      	stage.addChild( tile3 );
      	// You get the idea, it is very lengthy!
      }
      

      getDefinitionByName() allows you to solve this problem!


      How to Use It

      Now, the above code was a little messy and we had to type many lines just to make a few different tiles. This is how we could achieve the same goal using getDefinitionByName():

      
      
      private function createTiles():void
      {
      	for( var i:int = 1; i < 8; i++ )
      	{
      
      		var tileRef:Class = getDefinitionByName( "Tile" + i ) as Class;
      		var tile:Sprite = new tileRef();
      		stage.addChild( tile );
      
      	}
      }
      

      In line 6, getDefinitionByName() returns a reference of the class called “Tile + the current value of i in the for-loop“. So, when i is equal to 1, getDefinitionByName("Tile" + i); returns a reference to the class Tile1. We then create the tile and add it to the stage.

      (We can’t write var tile:tileRef because tileRef does not refer to anything at compile time; if you try, you’ll get a compiler error.)

      However, when you run this code, it will not work! You’ll get a variable is undefined error message, in most cases, because “Tile1″ might not be enough information for Flash to find the class. Let’s look at some workarounds.


      Make It Work

      There are a few commonly-used methods to solve the problem of the variable is undefined error you’ll get when you run the above code, and I am going to teach you what they are. I would also like to give credit to Gert-Jan van der Well of Floorplanner Tech Blog for this blog post.

      Here are some of the methods you can use:

      • Use a Dummy Variable
      • Use short notation of Class name
      • Include the full path in the String
      • Include the class’s SWC in your project

      Using a Dummy Variable

      In this method, you just create some dummy variables with references to the classes you want to refer to with getDefinitionByName() later:

      
      
      private var dummyTile1:Tile1;
      private var dummyTile2:Tile2;
      //etc
      
      private function createTiles():void
      {
      	//Create the tiles
      }
      

      This works, but it is very ugly. If you have the Tile classes in another package, you would also have to import them!

      Short Notation

      This is much like the Dummy Variable method, but you don’t bother setting up a dummy variable for each class; you just drop a few explicit references to the classes themselves:

      
      
      Tile1;Tile2;Tile3;Tile4;Tile5;Tile6;Tile7;
      //etc
      
      private function createTiles():void
      {
      	//Create the tiles
      }
      

      Now, this may look neater, but the fact that you will have to update this list everytime you make a new Tile remains.

      Including the Full Path Name

      Another method, which is the tidiest (if you have classes in another package) is to include the full path name in your String:

      
      
      //Let's say my Tiles are all in the package 'project.Tiles'
      
      private function createTiles():void
      {
      	for( var i:int = 1; i < 8; i++ )
      	{
      
      		var tileRef:Class = getDefinitionByName( "project.Tiles.Tile" + i ) as Class;
      		var tile:tileRef = new tileRef();
      		stage.addChild( tile );
      
      	}
      }
      

      Much tidier! However, this only works if the classes are in a separate package from this class.

      Using a SWC

      If the Tiles are held in an SWC, you can make this much easier, without needing to use any imports or dummy variables. I would like to give credit to v0id from Dreaming in Flash for this blog post that explained to me how to use this method:

      • In the project properties choose “ActionScript Compiler”
      • To the “Additional compiler arguments” field, add the following: include-libraries PATH_TO_SWC

      The PATH_TO_SWC must be the absolute path and not the relative path!

      Great, all these methods have now been explained. Unfortunately, there are no fantastic magical methods to use if you have all the tiles in the same package as all the other AS Files. I would recommend you make a new package called Tiles or something if you want to use the good methods!


      Conclusion

      Well, today you should have learnt how to use getDefinitionByName() and the best methods of using it. I hope this will help you in any future projects and if you have any questions, leave them in the comments section below!


    10. Jordan McNamara says:
      October 30, 2011 at 5:03 am

      Hey folks! I’m Jordan McNamara, the Community Manager for Envato and I wanted to share some exciting news with you about the Envato Marketplaces.

      The 12th of September was an exciting day for me as a member of our thriving Marketplace community. I along with everyone here at Envato HQ watched eagerly as the global Marketplace member count steadily grew higher and higher and higher…

      We were close, very close to the 1,000,000 member milestone. With every page refresh the count grew higher and I began to reflect on just how staggering having 1,000,000 members really is and on just what a fantastic community everyone has helped to build here.

      I remember starting at Envato in 2009 and being blown away by the 250,000 or so accounts that were registered at the time. I bragged to all of my friends “Can you believe there’s a quarter of a million members!?”. Little did I know in just under two and a half years I would be writing this post!


      View the Infographic!

      To celebrate this exciting occasion we wanted to do something interesting and fun that celebrates the Marketplace community and the sites themselves. So with that in mind, we are proud to present the 1,000,000 Members Infographic! We’ve packed it full of fascinating facts and data about our members and our nine Marketplaces.

      Click to Jump to the Infographic!

      Every day we continue to be amazed at the astonishing speed our community of creative members grows. We’ve enjoyed every minute of the journey and are proud and thankful of the achievements our members have made. Here’s to the next million!

      View the Infographic!


    Leave a Reply

    Click here to cancel reply.

search search search search search
Find an Article
Categories
  • Flash Video Training
  • Hints and Tips
  • Recommended
Please Support Our Sponsors
Recent Posts
  • Workshop Coding Challenge: Fix This Breakout Game
  • Enable the Latest AIR SDK in Flash Professional CS5.5+
  • Quick Tip: Versioning Your Files With Dropbox (via Webdesigntuts+)
  • Workshop: Nuclear Outrun – Critique
  • Understanding Variables, Arrays, Loops, and Null: The Post-it Note Analogy
Tag Cloud
2011 ActionScript Active Activetuts+ Adobe animation Basic Basix Best Build Button Character Create Creating Critique Custom design Effect Effects Files Flash from Game Guide HTML5 Introduction Macromedia Motion Muzzle part Player Premium Professional Quick Silverlight Simple Text Tool Tutorial Tuts+ Tween Using Video website Workshop
About Our Site:

Hey there and welcome to "Flash Video Training Source", a resource for anybody interested in learning more about Adobe's great tool. We feature educational videos, which will help you master Adobe Flash and help you get to know all of its features. We at "Flash Video Training Source" believe that video training and video... more

Why don't you follow us on Twitter and get the latest video tutorials twitted to your account. Just click on the floating twitter bar to your right!

Go Back In Time
May 2012
M T W T F S S
« Apr    
 123456
78910111213
14151617181920
21222324252627
28293031  
Pretty Blank Box
top

Blogroll

  • Development Blog
  • Documentation
  • Plugins
  • Suggest Ideas
  • Support Forum
  • Themes
  • WordPress Planet

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

Archives

  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
Powered by WordPress  |  Designed by Elegant Themes  |  Lightning Fast Hosting by Site 5 Hosting