In this article, you will learn a much better, more object oriented way to structure a Flash application than the typical "frames on the main timeline" scheme that you see so often everywhere. This better way might be summed up in a few words as "MovieClips as screens." This is so far superior to "Frames as screens," that I think you will be as amazed as I was, when I line out for you exactly why. Read on!
You have no doubt seen a lot of timeline navigation systems if you have looked at the files of others, flash templates, or maybe you've even created them in your own files. I used one myself to illustrate button disabling in one of my other tutorials. Typically there will be a labeled frame for each "screen," and buttons that trigger "gotoAndStop" commands on the main timeline. Each destination frame will then also have a keyframe in the actions layer with the actions for that frame.
While this works, it has at least a few fairly serious problems. The first is that whenever flash's playhead is told to gotoAndStop on one of these labeled keyframes, any code on that frame will run, even if it's run previously. That is, any code that's not enclosed in a function will run automatically when the playhead hits that frame. This means that if you navigate away and come back again you will find that the frame has reset itself. This may or may not be what you want, but to have any control over it requires jumping through some serious hoops (for example, one way is making sure you visit a given frame only once, and then after that, never navigate to that frame again, but rather to the following frame).
The second problem is that functions and variables defined in earlier frames are still active in later frames. They are all still active unless they are specifically turned off. This becomes problematic when some of those functions might be listeners that have been added to buttons. If you aren't careful, you can have the user navigating to another frame where the listener is still active, but the button it was added to is gone. A very common problem is attempting to add an event listener to a button that isn't there, which results in a null object reference error. That means that you have to keep buttons around on all frames, and control their access with their "visible" property. Worse still are ENTER_FRAME events that may be running when the user switches frames. It's very easy for an ENTER_FRAME handler function to refer to screen objects that aren't there any more when the frame changes. More null object errors! It is therefore very important to remove listeners that were defined on a given frame when you navigate to another frame.
These two problems both contribute to and are compounded by a third problem: it cannot be known in advance which frame a user is likely to navigate to, or from. Of course, ideally you would like an interface that can be navigated in any order. But the more frames and screens you create, the worse the upkeep gets. For example, on a certain frame, you might have a button, or several buttons, that potentially navigate you away from there. Let's suppose that frame has an ENTER_FRAME event running. You would have to make sure that every possible exit from that frame removed that listener first. This makes for a lot of repetitive code!
Alternatively, you might decide to just remove that listener on your other (destination) frames. Again, more repetitive code. In fact, since on a given frame you don't have any way of knowing which frame was being navigated from (that is, short of dreaming up some elaborate custom system), you don't even know if that ENTER_FRAME listener is still in force or not. Believe me, while these problems can all be overcome, they are overcome by jumping through even more hoops, and the upkeep gets to be a nightmare. Every time you want to add something to your file, you have to consider the potential implications of all that frame switching all over again.
Here's a typical timeline navigation system:

Have I painted a dismal enough picture yet? Frame switching, turning event listeners on and off, controlling visibility of buttons so that they are always around (to avoid that dreaded error #1009 null object reference). Pretty soon all you are doing is keeping house instead of really innovating and being creative.
The problem that's at the heart of all these other problems is that keyframes on a timeline do not have their own scope, however much it might seem like they should. A "scope" is a section of memory where variables live, where each variable must have a unique name. If two variables have the same name and you don't get error messages when the code runs, then you know they are in different scopes. If you try using the same variable twice in the same timeline (except for local variables inside functions, of course), you will get an error message. Keyframes are not their own scope, the timeline is the scope.
Well, I'm sure I've probably outlined the problem enough. For the rest of this article we will explore a much better way to structure a flash application.
"Frames as screens" is out. "MovieClips as screens" is in. And OOP is in, too! Read on!
First of all, there is really no strict definition of what constitutes a "screen," but what I mean in this article when I refer to a "screen" is the layout as the user sees it. Screens, for example, might be web pages in a website, levels in a game, sections or modules of a program, etc, etc. Whenever you want to switch the display, you want to change screens. The whole concept is very informal and the definition flexible, and should be taken that way.
If you have ever written anything more than the simplest one screen flash program, you have probably been faced with the problem of how best to deal with multiple screens. Not only the screen switching, mind you, which is formidable, but also the idea of extensibility. That is, later you might want to add more screens to your application, or even take a screen from one application and bring it into another.
What I am going to show you is a structure that will allow you to do all of that, and more! The structure is not that hard to build, either! In fact, it's so simple you may wonder (like I did!) why you didn't see it before. It's completely modular, because unlike my former example of the timeline navigation, each part is definitely its own scope.
One last word about timelines and then I'll shut up about them: I'm not saying that you should never use timelines for anything, either. Use them for animation, and object "states" (for example, a deck of cards MovieClip with 52 frames with a different card on each frame, or as you'll see, the Tooltip MovieClip we are going to build), just don't use a timeline for your overall navigation. That said, let's get started!
We're going to create a simple drag-and-drop game, and it will have three screens: Intro, Learn, and Drag. Each screen will have a linked Class file with those same names: Intro.as, Learn.as, and Drag.as. Finally, there will be a document class named Main.as. I will show step by step how to build this structure into a finished application.
In Flash, create a new FLA file. Click the gray area around the stage, this will make sure the properties panel gives you the document properties. Change the frames per second to 30. Change the stage dimensions to 800 wide by 500 high. Then, let's save the file. Choose File, Save. In the process of saving the file, create a new folder somewhere in your file system (this part is up to you) and call it "structure." Save the file as "good_structure.fla."
Now we are going to make our first screen. From the Insert Menu, choose "New Symbol." Make sure the MovieClip symbol is selected, and name it "Intro" for a library name. Check the box that says "Export for Actionscript." The second box, "Export in frame 1" should already be selected also. Next, accept the default class name "Intro," which was named after the library name. Click OK.

Click OK on the following warning:

Now we are in the symbol's editing mode where we can design our first screen. The first thing to do is to scroll with the scroll bars until the little + sign is somewhere in the center of the screen. This is the registration point of the symbol. This will also be the center of the screen we are designing. Choose the rectangle tool. Draw a rectangle of any size and color, but with no stroke. Next, click it to select it. In the properties panel, give it a width of 800 and a height of 500. Next, use the align panel with the "to stage" button selected to center the rectangle horizontally and vertically. Double click the layer name, and type in "bg," then lock it. This layer will be the background, but will also make a handy visual representation of the stage. We're keeping the registration point in the center so that we can potentially center objects on the stage with the align panel (with the "to stage" button pressed in).

This will be an introduction screen for the application. We are going to make a very simple drag and drop game. On this introduction screen we are just going to have a couple of static textboxes for titles, some instructions on how to play the game and two buttons: "Instructions" and "Play the Game." I'm also jazzing up this screen with some graphics provided by my good friend, Jessamin Swearingen.
What I did to design this screen was just to start with the background layer. When the background was in place, I locked the layer and created a new one. Then I brought in the coach froggy graphics from another file, scaled them slightly, named that layer "coach froggy" and then locked it.

Then I created a new layer for the titles, and put some static textboxes out there with my title text. I named that layer "titles" and locked it. Next, I created a new layer for the buttons. I again just put some static text out there, "Instructions" and "Play the Game" and I selected and converted each of those into a button symbol. I game them instance names in the properties panel of "instructions_btn" and "playGame_btn" respectively. Finally, I locked that buttons layer and made a new layer for the instructions. I put some more static text out there with some (very!) basic game instructions, using two more static textboxes. I then selected both of these and converted them to a single MovieClip symbol, and I gave the instance on the stage the instance name "instructions_mc."

I don't expect you to follow along on this screen design part, this is just for your reading and understanding as I describe the process I went through. I will assume that designing screens is something that you already know how to do, and chances are you are better at it than I am anyway. The main thing to know about this screen is that there are three symbol instances with the instance names instructions_mc, instructions_btn, and playGame_btn, and we need to make note of that so we can program them.
Now that we have designed the screen, let's create a class file for it. In Flash, choose File, New, then "ActionScript File." This will open up a new file where we can write our class:
package {
import flash.display.*;
import flash.events.*;
public class Intro extends MovieClip {
public function Intro() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
}
private function addedHandler(event:Event):void {
}
private function removedHandler(event:Event):void {
}
}
}This is the basic structure that we will use for all of our screens. Let's take it apart line by line, shall we? The package statement is required, as all classes must reside in a package, although in this case we are not using a named package. You may know that package names must correspond to folder names on your hard drive, and that the first folder named must reside in a classpath folder. However, since we aren't naming any folders, we need not worry about that, but you should know that consequently this Class file must itself reside in a classpath folder. Putting it in the same folder as the FLA file accomplishes this nicely. See my first tutorial, "Make your own reusable classes using Flash and AS3" if you want to know more about packages and the classpath.
Import statements are required for each and every "built-in" class that your class will use. Importing all the flash.display classes and all the flash.events classes with a "*" is not a bad idea, because you will almost always need those two packages and these two lines automatically take care of it. Next comes the class declaration. This class will be called Intro, and it extends MovieClip. That means that instances of this class will BE MovieClips. Next comes the constructor function (public function Intro). This is the function that will be called when outside code uses the keyword "new" to make a new instance of this class. Whenever that happens, event listeners will be added for the ADDED_TO_STAGE and REMOVED_FROM_STAGE events. Next, there are two functions, one for each of those listeners. Believe it or not, this part is crucial to our design. By setting this up like this, whenever outside code uses addChild() to add an instance of this class to the display, the addedHandler function will be called, and whenever outside code says removeChild() on an instance of this class, the removedHandler function will be called.
There will only be one instance of the Intro class, because our application will only need one Intro screen. But you should know that classes are like a stamp pad, so if you wanted to you could stamp out as many copies of this as you want. All you would have to do is keep using the keyword "new" to make more new copies.
Let's save this file to the same folder as the FLA file. It MUST be named "Intro.as". After you save it with that name, and since you are saving it to a folder that's in the classpath, it will automatically be linked to the library symbol in the FLA, because you'll recall that we gave that library symbol that same class name.
Right click the Intro symbol's listing in the library again, and choose "Properties." In the symbol properties dialog box, look to the right of the Class field. Click the button that looks like a check mark. You should get a dialog box that tells you "the definition for this class was found at..." and then the file path. Clicking on the pencil button next to the check mark button will open the class file in Flash for you to edit. So the symbol and the class are now linked: the symbol is the class and the class is the symbol! Later we will return to this class and edit it some more. For now, let's return to it and save it again. Without changing its contents, use File, Save As and save it as "Template.as." Even though that name doesn't match the name in the file, we won't be using "Template.as" as an actual class, but rather just as a device to save us some typing. We can open "Template.as," change the word "Intro" in two places, and then immediately save it under another name.
Next, we need to write a document class for our FLA file. In flash, choose File, New, and then ActionScript File. Type in the following:
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
public function Main() {
trace("works");
}
}
}
This is almost the same structure as before. One difference is that we don't need the ADDED_TO_STAGE and REMOVED_FROM_STAGE event listeners here. The reason is that we are going to be setting this class as the document class. The document class represents the main timeline of the FLA file. As such, it has already been added to the stage automatically. In other words, there will never be a need to detect when this object is added or removed from the stage. It's already on the stage. In fact, this will be the controlling code I talked about earlier, that will be doing all the adding and removing of the other screens!
Inside the constructor, I have just placed a trace command that is simply to get feedback that the document class is indeed linked to the fla file and working properly.
Save the above file as Main.as. Once again, save it to the same folder as the FLA, of course. Next, let's link this file to the FLA file. Go back to the FLA file in Flash, and click somewhere on the area around the stage. This will select the FLA document itself, so that the properties panel says "Document" at the very top.

In the Class field, enter the word "Main" (without the quotes, and don't include the .as file extension.
Press CTRL-ENTER to test the movie. You should get the word "works" traced to the output window. Now we can proceed to use this document class for better things! So, back in the Main.as file, go ahead and delete the trace command. We will now instead use the document class to create an instance of our Intro screen, and put it on the display with addChild(). Notice that we list the variable in the variables list. That way, we can use the variable inside of other functions throughout the class:
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
private var intro:Intro;
public function Main() {
intro = new Intro();
addChild(intro);
}
}
}
Again, save the file, and test the movie. You will notice that the screen does indeed get added to the display, but only occupies the upper left hand quarter of the stage. Three quarters of it is off the stage to the upper left!

This is because we designed the screen with the registration point in the middle. While this makes designing easier (because we can use the align panel for centering things on the stage), on the developing side it might be better if the whole screen had its registration point in the top left. But we can also keep our centered registration point, and just adjust the x and y values with code to center it on the stage. In fact, this will be a great test of the ADDED_TO_STAGE handler in the Intro.as file.
Edit the Intro.as file again. Add the following two lines to the addedHandler function:
package {
import flash.display.*;
import flash.events.*;
public class Intro extends MovieClip {
public function Intro() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
}
private function removedHandler(event:Event):void {
}
}
}
The word "this" is a reference to the object itself. When objects are created from this class, each object gets a copy of all the code in this class. So for each instance created (the word "instance" and "object" are synonymous), the word "this" is a self-reference. Like I said before, there will only be one instance of this screen created, but you should understand how the keyword "this" works. The word "this" does NOT mean this class, it means this instance. The word "this" could also have been omitted, in which case it would be "understood" :
x = stage.stageWidth / 2; y = stage.stageHeight / 2;
These two added lines would not work if you were to place them in the constructor. You are welcome to place them there, save the file, and give it a try. You will get the notorious "null object reference error." Let's understand why. First, our class inherits from the MovieClip, so it is a MovieClip. As such, it inherits all of the properties and methods of the MovieClip class. One of those properties is the "stage" property. The stage property for a MovieClip instance that is not on the display list will have a null value. There are two ways to get a MovieClip instance onto the display list: by dragging it physically from the library to the stage at authoring time, and by creating it with code and adding it with code, specifically the addChild() command. In either case, the stage property goes from being null, to being an actual reference to the main stage object. But in the case of the one that is created with code, there is that small space of time in between the object's creation (the "new" command) and it's being added to the display list (the "addChild" command) during which the stage property is still null. But the moment the object is added with addChild(), its stage property is "filled out" (or given a value) and now refers to the actual stage. It is the ADDED_TO_STAGE event that detects this very moment that the addChild happens from the outside code.
So, to sum up, and to make it relevant to what we are doing, when the controlling code says:
intro = new Intro();
...the constructor of the Intro class runs. And when outside code says:
addChild(intro);
...the addedHandler function inside the Intro class is triggered and runs. In our application, we will only be using the new command once, as we will only be making one instance of the Intro screen. But addChild() and removeChild() might run any number of times, depending on how many times the user clicks the buttons to switch screens. Having these events that detect when addChild() and removeChild() happen from the outside means that we can do whatever kind of housekeeping we want inside these functions.
Unlike timeline frames, class files are a scope of their own. This means that you can reuse the same variable names and function names in your other classes. Only the variable names and function names within the same class have to be unique from each other. This is what makes things all very nice and modular. With a few minor changes to the document class, you can expand your program by adding more screens, or taking them away. There is never a need to worry that things might clash with each other. Classes are all nice and encapsulated that way. By default, they don't share information with each other, and later I will show you how to make them share with each other just the information you want.
Now, armed with the knowledge of what happens and when, let's put it to some good use. The first thing we need to accomplish is that the instructions need to be hidden by default, and the user must click the button to display them. So here is the Intro class again, with a new line added to the constructor:
package {
import flash.display.*;
import flash.events.*;
public class Intro extends MovieClip {
public function Intro() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
instructions_mc.visible = false;
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
}
private function removedHandler(event:Event):void {
}
}
}
The reason we are able to call instructions_mc by name is that we gave it that instance name. When you type instance names in the properties panel, the Flash compiler does you a favor and creates matching variable names that you can use in your code. It also automatically declares them for you (their instantiation is something you did by creating them manually at authoring time). This whole process is invisible to you, and all you need to care about is that it works. The new line of code in the constructor sets the visible property of the instructions to false so that the game instructions do not display initially. Remember, the constructor only runs when outside code says "new," so in our case, this instruction will only run once, because we don't intend to instantiate this class more than once.
The next thing to do is to make the instructions visible again when the instructions button is clicked. Therefore, we need to add an event listener for the CLICK event to this button, and also write an event handler function for it:
package {
import flash.display.*;
import flash.events.*;
public class Intro extends MovieClip {
public function Intro() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
instructions_mc.visible = false;
instructions_btn.addEventListener(MouseEvent.CLICK, showInstructions);
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
}
private function removedHandler(event:Event):void {
}
private function showInstructions(event:MouseEvent):void {
instructions_mc.visible = !instructions_mc.visible;
}
}
}
In fact, the one line of code in the handler reverses the "visible" property of the instructions_mc using the not (!) operator. This will make the button work like a toggle, turning the instructions on and off. After making this change, save this file and test the movie. I find that pressing CTRL-S (save) and CTRL-ENTER in rapid succession makes a cool way to save and test.
The other thing we must do is to make the "Play the Game" button change the screen. So add this line to the consructor of the Intro.as class file:
playGame_btn.addEventListener(MouseEvent.CLICK, playGame);
Next, we must of course proceed to write the playGame function. Here we come to the horns of a dilemma, because the controlling code needs to be the code that changes screens, but the button is located in the screen that needs changing. So Intro screen's class needs to somehow tell the Main class to remove it and add the Learn screen. Of course, we haven't created or instantiated the Learn screen yet, but let's write the screen changing code anyway. We will write the screen changing code in a public function in Main.as, then call that public function from the Intro.as class. Here's the code in the Intro.as file:
private function playGame(event:MouseEvent):void {
MovieClip(parent).gotoLearn();
}
Now, we must proceed to write the gotoLearn function in the Main.as file:
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
private var intro:Intro;
public function Main() {
intro = new Intro();
addChild(intro);
}
public function gotoLearn():void {
removeChild(intro);
addChild(learn);
}
}
}
The only trouble with this code is that it isn't flexible enough. The reason is that as we come up with more screens, we would like to use this same function to change to the learn screen from any of those other screens, not just intro. Later there will be a Drag screen, and we are going to make it possible to go back and forth from the Drag screen to the Learn screen. The removeChild() function is finicky--if you ask it to remove something that is not on the display list, you get a nasty error message. If we ask to remove the intro screen, for example, but the intro screen is not the one current being displayed, we will run into this problem head first.
One solution to this is a favorite trick of mine: the introduction of a "blank" MovieClip variable that can take on the identity of any of these other screens. I will choose to call this variable "currentScreen." The idea is simple: whenever there is a new screen shown, that screen becomes the currentScreen. Then when we say removeChild(currentScreen), we will always be removing whichever screen was the most recent one displayed. Here's the main class again with these changes:
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
private var intro:Intro;
private var currentScreen:MovieClip;
public function Main() {
intro = new Intro();
addChild(intro);
currentScreen = intro;
}
public function gotoLearn():void {
removeChild(currentScreen);
addChild(learn);
currentScreen = learn;
}
}
}
If you try to test the movie at this point, you will get a compiler error, naturally, because we have referred to a learn object that doesn't exist (yet)! On the next page we will proceed to create and develop it.
Back in the FLA file again, let's create a Learn screen. This will be a screen where students can move their mouse around and read a tooltip as they hover over the various continents. So I will show you how to create this tooltip also (bonus!).
Double click the Intro MovieClip in the library to go into its edit mode. Unlock the bg layer. Right click on its one single frame and choose "Copy Frames." Lock the layer again and go back to Scene1. From the Insert menu, choose "New Symbol..." In the Create New Symbol dialog box, type "Learn" in the Name field, and make sure the type says MovieClip. Check the box that says "Export for Actionscript," and just like before, accept the class name "Learn," click OK and then click OK again on the next dialog.
Now we are in the editing mode for our Learn screen. Click on the first frame in the timeline to select it. Right click it, and choose from the menu "Paste Frames." This will paste the background we borrowed from the Intro screen, and it will even name the layer for you. Go ahead and lock the layer, and make a new layer above it. Go back to Scene1 once again. This isn't much of a Learn screen, but it is enough to make our code work, and we will refine it more later.
Now let's proceed to create Learn.as. First, open Template.as. Let's copy and paste these lines from Intro.as:
this.x = stage.stageWidth / 2; this.y = stage.stageHeight / 2;
These should be added in to the addedHandler function of Template.as. Template.as should now read:
package {
import flash.display.*;
import flash.events.*;
public class Intro extends MovieClip {
public function Intro() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
}
private function removedHandler(event:Event):void {
}
}
}
Go ahead and save Template.as (CTRL-S). The thing is, this centering code is something we are going to need in every screen we make, as long as we keep making screens with the registration point in the center. So it might as well be part of the Template file.
Next, change the word "Intro" to "Learn" in the two places where it occurs. Handy hint: You can double click a word in your code and the editor will automatically select that whole word. Another handy hint: I use the search and replace feature a lot. Highlight a word, click the little magnifying glass on the toolbar, and you can replace every occurance of a word with a different word. I always use the "Replace" and "Find Next" buttons so that I don't accidentally change something I didn't intend to. Anyway, now click File, Save As... and save this file as Learn.as.
Now go back to the Main.as file. Add this line in the variables list:
private var learn:Learn;
Add this line to the constructor function:
learn = new Learn();
Here's the latest version of Main.as after making these changes:
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
private var intro:Intro;
private var learn:Learn;
private var currentScreen:MovieClip;
public function Main() {
intro = new Intro();
addChild(intro);
currentScreen = intro;
learn = new Learn();
}
public function gotoLearn():void {
removeChild(currentScreen);
addChild(learn);
currentScreen = learn;
}
}
}
The learn screen is currently blank except for the background. Our goal here is going to be as follows: We want to provide a world map with individual continents. Each continent will be a separate MovieClip instance, so that we can program some rollover and rollout actions for each one. We also want to create a tooltip MovieClip that will follow the mouse pointer. Finally, we want to provide a button that will link to the other screen we are going to make, the Drag screen.
Once again, I have imported some artwork from another file. I made a new layer called "continents," and I made a MovieClip symbol for each of the six continents in the illustration (Yeah, I wondered what happened to Antarctica, too! Apparently it's expendable these days). Once again, I am not going to go into detail about how I made selections to the artwork and created the MovieClip symbols, and put together this screen, as I figure you probably already know how to do these things yourself. Anyway, I gave all the instances on the stage instance names, too: northAmerica, southAmerica, europe, asia, africa, and australia.

Next, let's create a Tooltip MovieClip symbol. Click the Insert Menu, "New Symbol..." (this will take us out of the edit mode for the Learn screen, and into the edit mode for the Tooltip symbol). Name it "Tooltip." No need to export it for actionscript this time. Using the Text tool, create a static textbox and type "North America" in a fairly large font. Using the align panel, center this textbox vertically, and position it a little ways up from the registration point. In my file, the y position wound up being -85:

Next, click on the timeline at frame 10, and press F6 (insert keyframe). This will copy the previous keyframe, textbox and all. Continue to do this four more times, making new keyframes at frame 20, 30, 40, and 50. Next, click on frame 60 and press F5 (insert frame). You will need to edit each of the textboxes on all of the frames to reflect the names of the six continents: North America, South America, Europe, Africa, Asia, and Australia.
Next, add a new layer for frame labels. Name the layer "labels." Put keyframes along the timeline in the same locations. Label the keyframes after their continents: "northAmerica," "southAmerica," "europe," "africa," "asia," and "australia." One last thing: Make a new layer, name it actions, and put a single "stop()" command on the first frame, in the actions panel (F9):

Now we are done editing the Tooltip MovieClip. In the library, double-click the Learn symbol again. Add a new layer to the timeline and name it "tooltip." Drag an instance of the tooltip MovieClip we just created to the stage. You can place it anywhere you want, as we will be moving it around the screen with code. Give it an instance name of "tooltip."
Next, let's bring in some buttons for navigation: a "learn" button and a "drag" button. I brought these buttons in from a different file (once again), and gave them instance names of "dragBtn" and "learnBtn." These "buttons" are actually MovieClip symbols. They will work just as well, but we will have to set their buttonMode property to true so that they get a hand cursor when they are rolled over. And actually, that won't be necessary in the case of the learn button, as it's just there for looks. We're actually going to dim it to half alpha and not add an event listener to it, so that it is disabled. That's because the learn screen is the one we're on right now. Anyway, here's what the finished Learn screen's timeline looks like:

With everything in place and having instance names, now we are ready to work on the code. So let's turn our attention to the Learn.as file. The first thing to code is the tooltip following the mouse. But first we will make the tooltip invisible by adding this line to the constructor:
tooltip.visible = false;
Next, we will add an event listener to the addedHandler for the ENTER_FRAME event:
addEventListener(Event.ENTER_FRAME, everyFrame);
Let's also immediately remove the same event listener in the removedHandler:
removeEventListener(Event.ENTER_FRAME, everyFrame);
Next, let's write the handler function "everyFrame" :
private function everyFrame(event:Event):void {
tooltip.x = mouseX;
tootlip.y = mouseY;
}
Now the class should look like this:
package {
import flash.display.*;
import flash.events.*;
public class Learn extends MovieClip {
public function Learn() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
tooltip.visible = false;
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
addEventListener(Event.ENTER_FRAME, everyFrame);
}
private function removedHandler(event:Event):void {
removeEventListener(Event.ENTER_FRAME, everyFrame);
}
private function everyFrame(event:Event):void {
tooltip.x = mouseX;
tooltip.y = mouseY;
}
}
}
If you test the movie now, you will not see the tooltip following the mouse, because it's been made invisible. What we need to do is to cause the tooltip to become visible again whenever it rolls over one of the continents, and disappear again whenever it rolls out of one of them. To accomplish this, we need to declare an array, and then place all of the continents in it, so that we can run a loop and add some event listeners to all of the continent clips at once. Add this line to the variables list:
private var clipArray:Array;
Then, in the constructor, add this line, which will instantiate the array and "populate" it:
clipArray = [northAmerica, southAmerica, europe, asia, africa, australia];
Next, in the constructor, add the following for loop:
for(var i:int = 0; i < clipArray.length; i++) {
clipArray[i].addEventListener(MouseEvent.ROLL_OVER, over);
clipArray[i].addEventListener(MouseEvent.ROLL_OUT, out);
}
Now it just remains to write the event handlers named "over" and "out." The first thing to establish in these functions is that the tooltip should become visible on rollover and then invisible again on rollout:
private function over(event:MouseEvent):void {
tooltip.visible = true;
}
private function out(event:MouseEvent):void {
tooltip.visible = false;
}
Test the movie, and you will see this in action. However, we still need to make the tooltip switch frames according to whatever continent it is over. To accomplish this, let's make another array that has just the frame labels in it as strings. So in the variables list, add this line:
var labelArray:Array;
Then, add this line to the constructor function:
labelArray = ["northAmerica", "southAmerica", "europe", "asia", "africa", "australia"];
Now, let's turn our attention again to the over function. Now we have two parallel arrays. If we can find out the index into the clipArray, we can use that same index to make the tooltip gotoAndStop on the frame at the same index into the labelArray:
private function over(event:MouseEvent):void {
tooltip.visible = true;
var index:int = clipArray.indexOf(event.currentTarget);
tooltip.gotoAndStop(labelArray[index]);
}
The first line here just makes the tooltip visible, we have been over that already. The second line here sets up a temporary variable named index. Here we see a cool method of the Array class, indexOf(). The indexOf() method can search through an array for you and return to you the index number of whatever element you specify in the parentheses. Once we have that index number, we apply it in the next line to the other array. labelArray[index], therefore, is going to contain whatever string corresponds to the array element that was rolled over. If North America is rolled over, for example, the indexOf method will return 0. Here's how:
It may look cryptic at first, but it's really just a series of substitutions, and once you understand it completely, this kind of programming can be pretty powerful.
Test the movie, and you will see that the tooltip follows the mouse, and displays the name of whatever continent is being rolled over, just like a good tooltip should. :) Pretty cool, eh?
I should note here that while I have just made a very quick tooltip, with only the name of the continent displayed, yours could be much more elaborate than that if you want, with a background shape, and perhaps another static textbox with a paragraph of information about each one. All you have to do is edit the frames.
Here's the current state of the file with all the latest changes:
package {
import flash.display.*;
import flash.events.*;
public class Learn extends MovieClip {
private var clipArray:Array;
private var labelArray:Array;
public function Learn() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
tooltip.visible = false;
clipArray = [northAmerica, southAmerica, europe, asia, africa, australia];
labelArray = ["northAmerica", "southAmerica", "europe", "asia", "africa", "australia"];
for(var i:int = 0; i < clipArray.length; i++) {
clipArray[i].addEventListener(MouseEvent.ROLL_OVER, over);
clipArray[i].addEventListener(MouseEvent.ROLL_OUT, out);
}
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
addEventListener(Event.ENTER_FRAME, everyFrame);
}
private function removedHandler(event:Event):void {
removeEventListener(Event.ENTER_FRAME, everyFrame);
}
private function everyFrame(event:Event):void {
tooltip.x = mouseX;
tooltip.y = mouseY;
}
private function over(event:MouseEvent):void {
tooltip.visible = true;
var index:int = clipArray.indexOf(event.currentTarget);
tooltip.gotoAndStop(labelArray[index]);
}
private function out(event:MouseEvent):void {
tooltip.visible = false;
}
}
}
The final thing to accomplish here is to program the buttons. Add these lines to the end of the constructor function:
learnBtn.alpha = 0.5; dragBtn.buttonMode = true; dragBtn.addEventListener(MouseEvent.CLICK, dragBtnClicked);
The first line here sets the learnBtn to half alpha. The second line gives the dragBtn a hand cursor when it is rolled over, letting the user know that it is active. The third line adds an event listener to the dragBtn for the CLICK event. Next, of course, we need to write the function that was named in that line, so put this function at the end of the class after all the other functions:
private function dragBtnClicked(event:MouseEvent):void {
MovieClip(parent).gotoDrag();
}
The command here is telling the Main class to execute its gotoDrag() function. Since we haven't written that one yet, let's go back to Main.as and do that. Once there, you can actually copy and paste the gotoLearn() function there, and then just make a few changes to it. Specifically, just change the word "Drag" to "Learn" and the word ughout:
public function gotoDrag():void {
removeChild(currentScreen);
addChild(drag);
currentScreen = drag;
}
If you followed along and made these latest changes, then your files are all up to date. If not, have no fear, the files are all attached to this tutorial in a ZIP file. We have a few more things to do, though, before we can test our movie again.
Now the only missing element is that we don't yet have a Drag screen. By now I'm sure you know what's coming next. Let's copy the background out of the Intro symbol again, and use it to make a new symbol. Double click the Intro symbol in the library, unlock the background layer, and copy the first frame of that layer by right-clicking it again and choosing "Copy Frames." Next, from the Insert menu, choose "New Symbol..." Type in "Drag," make sure it's type is set to MovieClip, check "export for actionscript, accept the name "Drag" for a class name, and click OK, and then OK again. Once in edit mode, right click the first frame on the timeline and choose "Paste Frames." Next, go back to Scene1. This will be enough of a dummy screen to test our movie.
Open Template.as. Change the word Intro to Drag in two places, then save the file as Drag.as. Now the drag symbol in the library has this class linked to it, just like the others previously.
Go back to Main.as again. We need to write the code that declares the variable for the drag screen. Add this code to the variables list:
private var drag:Drag;
And then in the constructor, we need to create a new instance of the drag screen. So type this at the end of the constructor function:
drag = new Drag();
Save the file. Here's the latest version of Main.as:
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
private var intro:Intro;
private var learn:Learn;
private var drag:Drag;
private var currentScreen:MovieClip;
public function Main() {
intro = new Intro();
addChild(intro);
currentScreen = intro;
learn = new Learn();
drag = new Drag();
}
public function gotoLearn():void {
removeChild(currentScreen);
addChild(learn);
currentScreen = learn;
}
public function gotoDrag():void {
removeChild(currentScreen);
addChild(drag);
currentScreen = drag;
}
}
}Now you can test the movie. You will be able to navigate to the learn screen, then to the drag screen. You won't be able to navigate any further though, until we further develop both the Drag screen and the Drag.as class file, which we will do on the next page.
Double click the Learn symbol in the library. Unlock the buttons layer if it's locked. Right click the first frame and choose "Copy Frames." Lock the layer. Next, double click the Drag symbol in the library. Add a layer above the bg layer. Right click the first frame and choose "Paste Frames." This will copy the buttons layer to this new symbol, and the buttons will retain their instance names, which were "learnBtn" and "dragBtn." They also retain their positioning, too, which saves us from having to do that work over again.
Before we do anything else, let's fix the navigation of our file. Let's turn our attention to the Drag.as file. Now we can progam these buttons, and we can even borrow and modify some code from the Learn.as file, and since these buttons have the same instance names, we only have to make a few changes. Here's the code copied from Learn.as (remember these lines?):
learnBtn.alpha = 0.5; dragBtn.buttonMode = true; dragBtn.addEventListener(MouseEvent.CLICK, dragBtnClicked);
And here's how we need to change them:
dragBtn.alpha = 0.5; learnBtn.buttonMode = true; learnBtn.addEventListener(MouseEvent.CLICK, learnBtnClicked);
Place them in the constructor function, right after the other things that are there. Next, we will copy this function from Learn.as:
private function dragBtnClicked(event:MouseEvent):void {
MovieClip(parent).gotoDrag();
}
Paste it into the Drag.as file, but modify it so that it reads:
private function learnBtnClicked(event:MouseEvent):void {
MovieClip(parent).gotoLearn();
}
Save the file, and test the movie. You should be able to fully navigate the program now, although the Drag screen is still blank except for the navigation buttons. But now you can go back and forth between the Learn and Drag screens.
Let's work on the drag screen some more. Add a new layer, call it "words." This is where we will create some movie clips with the names of the continents that the user can drag around the screen. When they are dropped on the correct continent, they will "snap" into place. I made some more static textboxes, typed in the name of a continent into each one, then converted each one to a MovieClip symbol. Then I gave each instance on the stage an instance name in the properties panel, using the same instance names I gave the continents, but with the suffix "_word" appended.
Next, let's do some more borrowing from the Learn screen. Double click the Learn screen in the library. Unlock the "continents" layer. Right click the first frame there and choose "Copy Frames." Lock the layer again. Double click the Drag symbol in the library. Add a layer to its timeline. Right click the first frame and choose "Paste Frames." Lock the layer. Lock every layer except the words layer. Drag the words layer to the top of the layer stack. What we need to do now is drag the words around the map and label the continents with them, then take note of each one's x and y position. These will be the positions to which the label clips will "snap" when they are dragged and dropped on the correct continent. What I usually do is make note of all these positions by writing them down on paper.

Having taken note of all the destination positions, you can now drag all the words back to the left side of the screen. Their start positions can just resemble a list, although you could actually mix them up and place them anywhere you want on the screen. This screen is now ready to program, so let's turn our attention back to the Drag.as file.

(Here you can see that the word clips have been arranged like a list on the left hand side of the screen).
In the Drag.as file, add these lines to the variables list:
private var wordArray:Array; private var matchArray:Array;
Add these lines to the end of the constructor function:
wordArray = [northAmerica_word, southAmerica_word, europe_word, africa_word, asia_word, australia_word]; matchArray = [northAmerica, southAmerica, europe, africa, asia, australia];
Next, we need to set up a for loop to add dragging behavior to each of the items in the wordArray. So add this to the end of the constructor, too:
for(var i:int = 0; i < wordArray.length; i++) {
wordArray[i].buttonMode = true;
wordArray[i].addEventListener(MouseEvent.MOUSE_DOWN, dragMe);
}
The next thing to do is write the dragMe function. Add this function to the end of the list of functions:
private function dragMe(event:MouseEvent):void {
event.currentTarget.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, dropMe);
}
Remember that event.currentTarget refers to whichever clip got the MOUSE_DOWN event. So this same listener will work for all of the clips. It will make whichever clip got the MOUSE_DOWN event to start dragging. However, when you program drag and drop, you need to add the MOUSE_UP event to the stage. This is to solve the following problem: If you add the MOUSE_UP event to the clip itself, someone might drag faster than the clip can keep up. If the mouse is released and the mouse pointer is not over the clip, you will be stuck in dragging mode. So we add the event listener to the stage instead. But solving this problem causes another one: When we write the handler for the stage, we can no longer use event.currentTarget to refer to the object being dragged. Why? Because event.currentTarget is going to refer to the stage, not the clip being dragged.
For this reason, I usually create a "blank" MovieClip variable to stand in for whatever clip is being dragged, and I usually call it currentClip. So add this line to the variables list:
private var currentClip:MovieClip;
Next, add a new line to the beginning of the dragMe function:
private function dragMe(event:MouseEvent):void {
currentClip = MovieClip(event.currentTarget);
event.currentTarget.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, dropMe);
}
Now, it just remains to write the dropMe function:
private function dropMe(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, dropMe);
currentClip.stopDrag();
}
So the scenario goes like this: When a clip gets a MOUSE_DOWN event, the dragMe function runs. That function sets it as the currentClip and starts it dragging. It also adds an event listener to the stage to listen for MOUSE_UP. This event listener is the dropMe function. When the stage gets a MOUSE_UP event, the dropMe function runs, and that function removes the listener that called it, and then tells the currentClip to stop dragging.
Test the movie now. When you go to the drag screen, you will be able to drag and drop all the word movie clips. Now all we need to to is make our program determine if the word is being dropped onto the right continent. We can do this using the hitTestObject method of the MovieClip class. We also need to create another array to store the x and y values we wrote down earlier. Let's do that first. Add this line to the list of variables:
private var posArray:Array;
Then, in the constructor function, add this line:
posArray = [ {x:-264, y:-88}, {x:-186, y:44}, {x:12, y:-144}, {x:-5, y:-27}, {x:157, y:-144}, {x:258, y:86} ];
This may look scary at first if you are not familiar with it. This is an array of objects. This is a very powerful technique. The objects are declared just by using the curly braces. Using curly braces is like saying "new Object()." This array of six objects holds the destination positions of the word clips. Since it is a parallel array (just like the other two), every element needs to correspond to the same element in the other arrays.The properties stored can be accessed later with just the array name, an index in brackets, a dot, and the name of the property, so for example:
trace(posArray[0].x) // would trace out -264
So what we need to do, in the dropMe function, is determine the index of currentClip in the wordArray array, test to see if it is hitting the corresponding element in the matchArray, and if it is, position it at the x and y location in the posArray. Also, take away its dragging behavior. However, if it's dropped and it isn't hitting its matching clip, we need to send it back to where it started from.
To send it back to where it started from, we need to record its starting position when it first gets the MOUSE_DOWN event. So let's create a couple more variables for the class. Add these to the variables list:
private var startX:Number; private var startY:Number;
Add these lines right after the first line of the dragMe function:
startX = currentClip.x; startY = currentClip.y;
This will record the starting position of whatever clip is being dragged, so that if it doesn't match when it is dropped, it can be sent back to wherever it came from.
Now let's finish off the dropMe function. Add these lines after the other two lines that are already there:
var index:Number = wordArray.indexOf(currentClip);
if(currentClip.hitTestObject(matchArray[index])) {
currentClip.x = posArray[index].x;
currentClip.y = posArray[index].y;
currentClip.removeEventListener(MouseEvent.MOUSE_DOWN, dragMe);
currentClip.buttonMode = false;
} else {
currentClip.x = startX;
currentClip.y = startY;
}
This is similar to what we did before on the Learn screen. We set up a temporary variable, "index," then use the indexOf method to find out the index into the wordArray of currentClip. Once that is found out, we can use it on the other parallel arrays. currentClip is tested to see if it is hitting the corresponding object in the matchArray. If it is, the same index number is used to position it. Then the "dragMe" event listener is removed from currentClip so that it can't be dragged again, and its buttonMode is set to false so that it doesn't get the hand cursor any more. It's out of the game. But if it doesn't match, the else block comes into play, which sends the clip back to where it started.
Here's the final version of Drag.as:
package {
import flash.display.*;
import flash.events.*;
public class Drag extends MovieClip {
private var wordArray:Array;
private var matchArray:Array;
private var posArray:Array;
private var currentClip:MovieClip;
private var startX:Number;
private var startY:Number;
public function Drag() {
addEventListener(Event.ADDED_TO_STAGE, addedHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
dragBtn.alpha = 0.5;
learnBtn.buttonMode = true;
learnBtn.addEventListener(MouseEvent.CLICK, learnBtnClicked);
wordArray = [northAmerica_word, southAmerica_word, europe_word, africa_word, asia_word, australia_word];
matchArray = [northAmerica, southAmerica, europe, africa, asia, australia];
posArray = [ {x:-264, y:-88}, {x:-186, y:44}, {x:12, y:-144}, {x:-5, y:-27}, {x:157, y:-144}, {x:258, y:86} ];
for(var i:int = 0; i < wordArray.length; i++) {
wordArray[i].buttonMode = true;
wordArray[i].addEventListener(MouseEvent.MOUSE_DOWN, dragMe);
}
}
private function addedHandler(event:Event):void {
this.x = stage.stageWidth / 2;
this.y = stage.stageHeight / 2;
}
private function removedHandler(event:Event):void {
}
private function learnBtnClicked(event:MouseEvent):void {
MovieClip(parent).gotoLearn();
}
private function dragMe(event:MouseEvent):void {
currentClip = MovieClip(event.currentTarget);
startX = currentClip.x;
startY = currentClip.y;
event.currentTarget.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, dropMe);
}
private function dropMe(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, dropMe);
currentClip.stopDrag();
var index:Number = wordArray.indexOf(currentClip);
if(currentClip.hitTestObject(matchArray[index])) {
currentClip.x = posArray[index].x;
currentClip.y = posArray[index].y;
currentClip.removeEventListener(MouseEvent.MOUSE_DOWN, dragMe);
currentClip.buttonMode = false;
} else {
currentClip.x = startX;
currentClip.y = startY;
}
}
}
}
Save the file, test the movie, and you will see it in action. If you drag a name to where it belongs on the map, it will snap into position, and be deactivated. If you drag a name to anywhere it doesn't belong, it will snap back to where you dragged it from.
Here's the finished SWF file, fully functional (even if it is a bit shrunken down for display purposes). You can click the buttons, get game instructions, switch screens, and drag continent names to where they belong. I realize of course that it's missing some of the things that would make it a truly finished game: a scoring system, a "start over" button, maybe a timer, some sounds, or other enhancements. What it did, though, is served the purpose of being a good, simple example file. You can download the ZIP at the end of this page.
While it may seem a bit disconcerting (for yourself or others) to open the FLA file and find that there is nothing on the stage and nothing on the timeline, I believe that if you try out this system and get used to it, you will find it far superior, and wonder how you got by without it. On the designing end, to work on a "screen" you will just double click it in the library. From there, you can do whatever layout work you want, creating more objects and giving them instance names. Then, on the developing end, you can edit the class file and use those instance names to program the behavior. The way things look and the way they act are wonderfully separated.
In your class, whatever you want to have happen up front (at object creation) should be put into the constructor. Whatever you want to have happen when the screen is added to or removed from the display should be put into the addedHandler or the removedHandler functions. What event listeners should you add and remove inside these functions? Any ENTER_FRAME handers, and any event handlers that were added to the stage, for sure. For example, you may have a screen that adds event listeners to the stage for keyboard events. You would want to add and remove such listeners whenever the object is added and removed from the display to keep them from firing when you don't need them. However, event listeners added to buttons need not be added and removed in this way. When they are not on the display, they can't be clicked on, rolled over, or interacted with anyway. Of course, it is ultimately up to you as to what kind of housekeeping you want to do with these added and removed handlers.
It's important to realize that instantiating an object with the "new" keyword puts it into the computer's memory. Adding it to the display with addChild just displays it. Removing it with removeChild just takes it away from the display. But removeChild doesn't remove it from memory. This is another great thing about this system: it gives you total control over what happens and when it happens. You will notice that unlike the system of timeline navigation I described at the beginning of this article, where things are reset when you revisit the frame, using this system you get to decide whether things get reset or not.
I will give you an example: Run the FLA file once. Click "Play the Game" at the Intro screen. Then, from the Learn screen, go to the Drag screen. Drag three continents to where they go and let them lock into place. Now switch back to the Learn screen. Now switch back again to the Drag screen. Notice that everything is just as you left it! That's because while the screen was not being displayed, it was still being held in memory. What if we wanted it to reset each time? Try this: Open Main.as and add a line to the gotoDrag() function that instantiates the drag screen all over again:
public function gotoDrag():void {
removeChild(currentScreen);
drag = new Drag();
addChild(drag);
currentScreen = drag;
}
That second line inside the function is the new one. Right after the currentScreen is removed, and just before the drag screen is added, I inserted a line to make the drag object instantiate again. This will make a totally new object, that new object's constructor function will run, and the drag variable will point to that new object instead of the old one. Everything will effectively be reset. Save this file and test the movie. This time, when you drag three continents to their place, then go to the Learn screen, then come back again, everything is reset. Knowing this, can you now imagine how you might easily make a "start over" button for this screen?
Since using this system is completely modular, when changes or updates are required, you will know exactly where to go to make them. Also, you can make your changes without them affecting everything else. Since classes have their own scope, if you like to use variable names like "currentClip," you can reuse them in your other classes. With hardly any hassle whatsoever, and with just a few edits to the document class, you can add or remove screens from your application at will. Coming back to applications later to update them becomes practically painless! Especially when compared to the "main timeline navigation" approach.
I have written this tutorial and included a lot of "how-to" stuff about things like tooltips, arrays, and drag-and-drop. But I also didn't want to get bogged down in that other stuff. What I mainly wanted to do is to create a simple application that would demonstrate the superiority this system of classes linked to library symbols for screen changing. In this tutorial we made a simple game with just a few screens. But you can take this system and apply it to any kind of application you want. The "screens" might be pages for a website application, for example. One screen might be your home page, another your photo gallery, yet another your contact page. The great part is that working on any one screen is isolated from everything else, and they can all be easily brought in or taken away from the overall system. So, your only limit is your imagination!
The screen switching can also be linear, or based on certain conditions, or whatever you want. For example, suppose our game had another level beyond the Drag screen. But we don't want to make that available until all the continents have been matched up. Just make a counter variable, increment it each time a match is made, and then don't show the button that advances to the next screen until all the matches have been made!
As always, I hope this article has helped to inspire you and spark your imagination, and I appreciate all of your comments. Happy coding!