In the previous section of this article, I illustrated how you can employ multiple listeners to respond to a single event in different ways. I also provided an example of creating a custom event, and showed how movie clips can dispatch events to indicate the completion of activity within the Timeline. This strategy makes it possible to call functions at a specific time, which is exactly what is needed for your preloader to accurately display the percent loaded graphically to the user. In the code examples, I also touched on the concept of using an import statement to make an external class available to the project. Now you'll take a closer look at ActionScript classes and see why modular coding practices are useful.
Classes are fundamentally just a different way of writing code. For the uninitiated, moving from simple coding to developing classes can be mortifying. Curiously, with experience, you'll find that ActionScript classes are far easier to read and understand than the simple, quick coding you're used to.
A simple class looks like this:
package
{
public class MyClass
{
var myVar:String // this is a class variable, otherwise known as a property.
function MyClass() { } // this is called the constructor. When you create new MyClass(), this function automatically runs.
function doSomething() {} // this is a class function, aka a method. I could call this to tell the class to quite literally do something.
}
}
Classes can utilize other classes by using import,
which I've already utilized on the Timeline. In our example, event hooks
imported com.bigspaceship.events.AnimationEvent.
There are many books that cover classes and object-oriented programming in Flash, but if I could point to one resource that helped me understand all of this, Colin Moock's Essential ActionScript 3 would be it. Colin's book is at my desk at all times.
PreloaderClip is a very specific kind of movie clip. It has
all of the regular movie clip methods (startDrag and stopDrag,
for example) and properties (x, y, and alpha), but it also has
its own custom functionality to handle loading.
In order to utilize the MovieClip class in this way, we'll need to extend it. Here's how to do that:
package com.bigspaceship.frameworks.site
{
import flash.display.MovieClip
public class PreloaderClip extends MovieClip
{
function PreloaderClip() {};
};
};
The key syntax is in line 4 above: extends MovieClip.
PreloaderClip extends the MovieClip class; it's a movie clip and then some. Makes
sense, right?
PreloaderClip has stop(); on the first
frame. That way the preloader artwork won't appear unless I command it to do
so. I want it to play that section with the frame label "IN" in the
Timeline, so I'll make a simple method to do that:
public function animateIn():void
{
gotoAndPlay("IN");
addEventListener(AnimationEvent.ANIMATE_IN,
_onPreloaderIn,false,0,true);
};
This method tells the timeline to play "IN" and
then listen for my event. When the event happens, it automatically calls _onPreloaderIn because that's the method I
specified to handle AnimationEvent.ANIMATE_IN. Here's what _onPreloaderIn looks like:
private function _onPreloaderIn($evt:Event):void
{
progress_mc.addEventListener(Event.COMPLETE,_onProgressBarComplete,false,0,true);
progress_mc.addEventListener(Event.ENTER_FRAME,_onProgressEnterFrame,false,0,true);
progress_mc.stop();
dispatchEvent(new Event(Event.INIT));
};
PreloaderClip assumes a movie clip is nested inside its
Timeline named progress_mc. I tell progress_mc to stop (in case it decides to
display the load progress before I'm ready) and listen to two new events: Event.COMPLETE and Event.ENTER_FRAME.
Now ENTER_FRAME is the new way to handle onEnterFrame: every frame _onProgressEnterFrame will be
called. (More on that in just a second.) Event.COMPLETE is an
event hook at the end of the progress_mc timeline. When progress_mc gets to the
end of that timeline, loading is finished.
That dispatchEvent line is an important one. Here
the PreloaderClip is telling any listeners that something happened. Just like
my event hooks, I can addEventListener() somewhere else to know when
the preloader is ready and to ensure that I can begin loading.
Let's look at the method I've mapped ENTER_FRAME to, namely _onProgressEnterFrame:
private function _onProgressEnterFrame($evt:Event):void
{
(_targetFrame > progress_mc.currentFrame) ? progress_mc.play() : progress_mc.stop();
var totalPct:Number = Math.round((progress_mc.currentFrame/progress_mc.totalFrames) * 100);
try
{
pct_mc.tf.text = totalPct.toString();
}
catch($error:Error)
{
Out.debug(this,"% loaded: " + totalPct.toString());
}
};
On every frame I'll compare _targetFrame against
progress_mc's currentFrame. If my target frame is greater than the
current frame, then play; otherwise stop. In addition, I wrote a simple way to
display the percent loaded in a text field to the user. If the text field
exists (in this case it should be nested in a movie clip named pct_mc and given
an instance name of tf), then it will write the total percent in
the text field. Otherwise I'll just trace the information to the Output panel.
The next part involves figuring out targetFrame. Here's how
this is accomplished:
public function updateProgress($bytesLoaded:Number,
$bytesTotal:Number, $itemsLoaded:Number, $itemsTotal:Number):void
{
var framesPerItem:Number = Math.floor(progress_mc.totalFrames/($itemsTotal-1));
var pct:Number = $bytesLoaded/$bytesTotal;
_targetFrame = Math.floor(framesPerItem * pct) + (framesPerItem * $itemsLoaded);
};
Ahhhhh, the secret is revealed. PreloaderClip actually doesn't load anything. It just displays the progress! I'll load the files I need from somewhere else—somewhere external. As data loads in, I'll tell PreloaderClip the following:
In other words, I'll
load each item sequentially. If I have an XML file, a SWF, and a JPEG to load,
I'll first load the XML. The current item I'm loading is 0 of 2 (remember,
always count from 0!). Then when the XML load is finished, I'll load a SWF.
That item is counted as 1 of 2. Finally, I'll load the JPEG file. The current
item I'm loading is 2 of 2. When the JPEG finishes loading, progress_mc will be
at the end of its timeline. The user will know that the content is 100% loaded
and PreloaderClip will receive the Event.COMPLETE event, which will automatically
fire _onProgressBarComplete():
private function _onProgressBarComplete($evt:Event = null):void
{
_isLoadComplete = true;
progress_mc.removeEventListener(Event.ENTER_FRAME,_onProgressEnterFrame); _animateOut();
};
private function _animateOut():void
{
gotoAndPlay("OUT");
addEventListener(AnimationEvent.ANIMATE_OUT, _onPreloaderOut,false,0,true);
};
In the function above, preloading is complete, so we play
the section of the timeline with the "OUT" frame label. When we hit
the event hook AnimationEvent.ANIMATE_OUT, _onPreloaderOut is
called:
private function _onPreloaderOut($evt:Event):void
{
// kill listeners. this will prep the loader for the next use.
// if i didn't do this, I might get _onPreloaderIn() twice when I reach the ANIMATE_IN event hook. that would mess everything up.
removeEventListener(AnimationEvent.ANIMATE_IN,_onPreloaderIn);
removeEventListener(AnimationEvent.ANIMATE_OUT,_onPreloaderOut);
if(_isLoadComplete) dispatchEvent(new Event(Event.COMPLETE));
else animateIn();
};
In the function above, I cleared the listeners out and
dispatched the Event.COMPLETE event. Now any listeners in my program will
know the PreloaderClip is finished and the Stage is clear to display the loaded
content.
I've got another class named Main, which will be my Document Root class. The Document Root is the main Timeline's class. When the SWF loads, the Document Root will be the controlling class. To set this, click anywhere on the Stage and then type the classpath to whatever class you want to use. In my case, I won't bury the Main class in a package; I'll simply call it Main and save my file to _classes/Main.as.
Let's take a look at Main:
public function Main()
{
Out.enableAllLevels();
Out.status(this,"Ready to go!");
// first let's start by setting our PreloaderClip to tell us
when it's finished. remember, Main is our main Timeline and preloader_mc is a
PreloaderClip.
preloader_mc.animateIn();
preloader_mc.addEventListener(Event.INIT,_onPreloaderIn,false,0,true);
preloader_mc.addEventListener(Event.COMPLETE,_onPreloaderOut,false,0,true);
};
private function _onPreloaderIn($evt:Event):void
{
_loadCount = 0;
// this is how to load SWFs, JPGs and such in AS3.
_swfLoader = new Loader();
_swfLoader.contentLoaderInfo.addEventListener(Event.INIT,_onSWFLoaded,false,0,true);
_swfLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,_onLoadProgress,false,0,true);
_swfLoader.load(new URLRequest("loadedContent.swf"));
};
When the constructor is called (that is, when the SWF
initializes), I know I'll have a PreloaderClip on my main Timeline named
preloader_mc. I'll tell preloader_mc to play its "IN" animation and
have Main listen for when it is finished. Then I'll set up a Loader, which will
do all the actual loading. The Loader will listen for load progress (ProgressEvent.PROGRESS), which will
fire Main's _onLoadProgress function. The function looks like this:
private function _onLoadProgress($evt:Event):void
{
Out.status(this,"_onLoadProgress");
// this is all we need to display load progress.
preloader_mc.updateProgress($evt.target.bytesLoaded,$evt.target.bytesTotal,_loadCount,_loadTotal);
};
The value of _loadCount is 0 when loading the SWF and _loadTotal is 1. I know this because I'm only loading two files. Therefore, PreloaderClip
will designate half of the actual progress to this SWF loading. When the SWF
finishes loading, Main is ready and waiting with _onSWFLoaded.
Here's what it looks like:
private function _onSWFLoaded($evt:Event):void
{
Out.status(this,"_onSWFLoaded");
_loadCount = 1;
// so now we can start to load the xml. here's how in as3.
// note that we can use the same _onProgress function for
both SWF and XML.
_xmlLoader = new URLLoader();
_xmlLoader.addEventListener(Event.COMPLETE,_onXMLLoaded,false,0,true);
_xmlLoader.addEventListener(ProgressEvent.PROGRESS,_onLoadProgress,false,0,true);
_xmlLoader.load(new URLRequest("content.xml"));
};
Now I'm loading an XML file, named content.xml. Note that the XML Loader is
calling the same method in Main to handle the progress: _onLoadProgress. One line of code in one function handles
all the visual updating. The only difference now is that the value of _loadCount is 1 instead of 0. The
progress movieclip is dedicated to 0–100% of total load.
When the XML file content.xml finishes loading, _onXMLLoaded is called. Here's what
the function looks like:
private function _onXMLLoaded($evt:Event):void
{
preloader_mc.setComplete();
};
That's it. The preloader knows
to finish the preloader and, as it animates out, Main is already listening for PreloaderClip.ANIMATE_OUT:
private function _onPreloaderOut($evt:Event):void
{
var loadedSWF:MovieClip = _swfLoader.contentLoaderInfo.content;
addChild(loadedSWF);
// the SWF is added to the stage.
loadedSWF.gotoAndPlay("IN");
// finally something looks familiar, right? the loaded SWF will play it's in label.
loadedSWF.addEventListener(AnimationEvent.ANIMATE_IN,_onLoadedSWFAnimateIn,false,0,true);
};
The loaded SWF is added to the Stage and begins to play. That's all there is to it!
How does Flash know where to find these external classes? By default, Flash looks in the base directory of your FLA file to find the classes. I personally dislike placing classes in this location. As a best practice, organize your classes into their own obvious directory. Here's how you can do that.
Select File > Publish Settings. Click the Flash tab and then click the Settings button next to the ActionScript version—which should be set to ActionScript 3, by the way (see Figure 2).

Figure 2. Clicking the Settings button to access the Settings dialog box
Upon clicking Settings, the ActionScript 3 Settings dialog box appears. Click the plus (+) button to add a new classpath. For this example, I'm going to put all of my classes in a directory relative to my FLA file named classes. So I'll type ./classes/ as the new classpath. (The trailing slash shouldn't matter.) After making these changes, the classpath is listed in my ActionScript 3 Settings (see Figure 3).

Figure 3. Adding new classpaths to the ActionScript 3.0 Settings dialog box
Figure 4 shows the (Mac OS) directory where my FLA is saved, so you can see where the _classes directory is located. All of my classes are stored in directories relative to the class package name. So if I have a class named foo.bar.MyClass, my class will be found at _classes/foo/bar/MyClass.as.

Figure 4. Development directory, complete with the FLA file and _classes folder
Generally it's considered a best practice to name classes beginning with the most general package name, and then make each subsequent directory more specific. I use com as the most specific class package possible. The directory bigspaceship contains the classes and packages built by/for Big Spaceship, frameworks contains all of the classes or packages that represent fundamental structures we use from project to project, and site contains classes specifically denoted for websites (as opposed to classes used for developing Adobe AIR applications or games). Of course, in this example all of the code is found in _classes/com/bigspaceship/frameworks/site/PreloaderClip.as.
Class package names are arbitrary; you can be as organized or disorganized as you like. When you work in a team environment, however, it's a good idea to adopt standards like this so that everyone on the team knows where to find the PreloaderClip. This ensures that your teammates don't waste energy making their own PreloaderClips. No sense reinventing the wheel, right?
In ActionScript 2, linkage was all about identifying a unique ID. In ActionScript 3, linkage is similar but different.
In ActionScript 3, each clip has a unique linkage ID as before. But ActionScript 3 also includes an additional parameter: base class. The base class is whatever type this linkage is. In our case, because we're extending the MovieClip class, it's of the type flash.display.MovieClip. A full discussion of extending classes is beyond the scope of this tutorial, but be sure to check out the ActionScript 3.0 Language and Components Reference for more information on extending classes.
So in our example the preloader is of type com.bigspaceship.frameworks.utils.PreloaderClip. Its base class is flash.display.MovieClip. If I wanted to add the preloader to the Stage dynamically, I could simply add the following code:
import com.bigspaceship.frameworks.utils.PreloaderClip; var p:PreloaderClip = new PreloaderClip(); addChild(p);
This code creates a new PreloaderClip, which I'll reference
via var p.
Next I'll add it to the display list via addChild. You may be
wondering, "What is the display list?" It's the new way that Flash
Player handles objects (movie clips, bitmaps, buttons, etc.). It is rather
complex and the full explanation of working with the display list is outside
the scope of this article.
At this point Flash knows that the clip I've placed on the Stage should be treated as a PreloaderClip, my custom class that extends the MovieClip class. I have my Document Root class all set up to leverage the PreloaderClip into displaying the progress. All I have left to do is compile the SWF file and relax.