Saturday, August 22, 2009

FlexSDK 3.3 How To Make a Flash PreLoader in AS3

One of the things that has been vexing me lately is trying to get a Flash PreLoader working for my games. I've tried Googling it, but there are too many like-terms: Flex Builder, FlashBuilder, Flex, CS3, CS4... The all have different methods, and my method is the least googleable: Flex 3.3 SDK, AS3 only.

I don't want others to run into the same problem, so here's a tutorial on how to make a flash preloader for AS3 with nothing more than notepad.

STEP 1: Why a preloader?

The first thing you have to do is arm yourself with knowledge. Why do you need a preloader?

As you may or may not know, every single SWF file is a movie. Even if it's a simple Hello World application, it is a movie with a single frame. It's hard to remember this sometimes, because working in the AS3 environment we don't have easy access to the stage's timeline, and everything we do happens on the first frame. Here's the key to remember, though: The entire current frame must be loaded before the frame starts to play. Most AS3 applications are built on a single frame (#1), which means your entire SWF must be downloaded, and loaded to memory, to display anything.

"But SWFs aren't all that big," you say. "Why would I need a preloader for that?"

The average connection speed in the United States is probably way lower than you think, or what you currently have. The fact that you are reading this blog at all probably means you have a 5mbps+ broadband connection. But that's how us geeks roll, and we only ever talk to other geeks. I'm on a 10mbps line with access to 50mbps if I wanted to pay extra for it.

The US has made several headlines recently beacuse the average broadband speed in the US is 1mbps. That is 128 kilobytes per second. But did you note the italicised term there? Over 60% of the United States does not have access to Broadband, so dialup is the connection type for many people. But what does that mean in real terms?

Here at Build 35 of Protonaut, the game is sitting at just under 600Kb - not counting audio assets. On the average broadband connection, it will take around 5 seconds to load and display the game to your screen. On a standard dialup connection, it will take over a minute. Many users will leave your page if they see a blank window for more than a second or two. 5 seconds of load time is absolutely painful, and is faster than the average connection!

STEP 2: Setting Up an AS3 Preloader

Allright, now that I've convinced you to use a preloader for your FlexSDK application, let's take a look at what is required.

First, you will need a whole new class file that will be your entire preloader. Here's some sample code for what I've used, and placed inside "Preloader.as" in the root directory of my project:
package {

import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.events.Event;
import flash.utils.getDefinitionByName;

[SWF(width='800', height='450', backgroundColor='#E5E5E3', frameRate='30')]

public class Preloader extends MovieClip {

public function Preloader() {
stop();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

public function onEnterFrame(event:Event):void {
if(framesLoaded == totalFrames) {
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
nextFrame();
init();
} else {
// Show your preloading graphic or animation here
}
}

private function init():void {
var mainClass:Class = Class(getDefinitionByName("Main"));
if (mainClass) {
var app:Object = new mainClass();
addChild(app as DisplayObject);
}
}
}
}
It's a very simple application. It simply adds an event listener that waits until ALL frames have loaded, and once that happens it will launch your "Main" class. At this point in the game, we still only have a single frame, so it won't do much. In this case, "Main.as" is my main class for Protonaut and also exists in the root folder of my project. You will have to edit this to match the main class name for your project - use "BobJones" if "BobJones.as" is the primary class in your project.

I've placed a comment where your preloading graphic, animation, advertisment - or heck - minigame, should reside.

If you compile and run this code alone, without following the extra steps below - the preloader will "blip by" in an instant and not show up until the whole SWF has loaded. So we aren't quite there yet, but we're prepared!

STEP 3: Reconfigure Your Compiler

Now that we have "Preloader.as" in place, we want to move the rest of your application off of Frame 1 and onto Frame 2. This will allow the preloader to show itself first, and wait on the loading of the second frame.

Here's a slice of my compiling instructions:
    "mxmlc.exe" -frame two Main -file-specs="Preloader.as"
They key statement here is "-frame two Main". This tells the compiler to move the class "Main" onto the second frame of the movie, and thus preventing it from loading before displaying the preloader.

For advanced users, note that you can specify any number of frames this way - possibly breaking up your application into several loading chunks, which can silently load in the background while people navigate your menus!

THAT'S IT!

STEP 4: Correcting for Errors

That's it, Unless of course you were referencing the Stage a whole lot in your code. Now that the concept of the "stage" resides in frame 1, the stage is outside of the scope for your "Main" class on Frame 2. Any references in your code to "stage" will now return Null, and probably cause you some headaches.

There's nothing stopping you from passing the stage to Frame 2 as a variable, though!

I dove back into Preloader.as and changed:
    var app:Object = new mainClass();
To:
    var app:Object = new mainClass(this);
Then, in Main.as's constsructor, I had it accept this as a variable and stored it for later reference. Couldn't be simpler!

STEP 5: Basking in your Glory

Now it's about time to crack open that beer because you're done. If you had any questions, or if this helped you at all, please leave a comment! I'll try to update this with any answers to questions people have.

2 comments:

  1. How does this work when the doc class has embed, etc? And is there an option to get progress percent?

    ReplyDelete
  2. I moved all my embed tags to the preloader and it works fine from there. Any variables you define from embeds and want to use in your main application will have to be passed to your Main app as a variable, as I did in Step4.

    And yes, a progress bar is easy! I setup a preloader animation that had 100 frames (just a progress bar filling up) and told it what position to go to with the following code:

    var getPercent = Math.round(loaderInfo.bytesLoaded / loaderInfo.bytesTotal*100);

    if (art) art.gotoAndStop(getPercent);

    ReplyDelete