Flash Performance Tips Part II
We were pretty stunned with the overwhelmingly positive response to the first part of this series. So stunned, we figured we should actually scrounge around and come up with some more useful tips.
In this next batch, we’re focusing on a blend of tips – we’re trying to specifically cover tips that will help you with your AS2 skills while simultaneously preparing for the transition to AS3.
UPDATE: Japanese translation is available here, again courtesy trick7.
Tweening Equations & Math Performance
There’s a lot to be said about programmatic motion. There’s tweening prototypes found all over the place (here and here, for example). Internally we use Adobe’s Tween packages. They’re simple and straightforward, and they perform pretty well without the need for anything complicated like a component.
That said, regardless of what you use to Tween you should consider the equations you run. Different ways of writing equations perform better or worse. Take this as an example – multiplying a decimal point benchmarks faster than dividing.
With that in mind, compare Adobe’s Strong versus Robert Penner’s Expo equation classes. The results are virtually the same – except Penner’s is far faster under stress. Before committing to an equation, sometimes it’s best to experiment and see if there’s a better solution out there…or a better way to write it yourself.
Kill Trace Actions
Not just for writing good, clean code but because tracing actually seems to bog down the Player. At least publish with trace actions Omitted. If you’re not using it already, look into the Out class. (link).
Basically the Out class has several different arbitrary “levels” of tracing: Warning, Info, Debug, Status and Fatal. It allows a developer to be more organized with their debug messaging.
We use Out for everything. As a project is winding down, we try to disable everything except the debug layer, so we can zero in on bugs for quick and easy squashing.
Optimize Bitmaps & Sound individually in the Library
It sounds painstaking (and sometimes it is), but by controlling the assets individually in your library you have much more control on the file size come compile time.
For sounds, we use AIF files and then convert them to MP3 on export. Better to start with a better quality sound and compress down than try to go up, right?
Oh, this is a good time to point out a bug with the OSX version of the IDE that continually frustrates me: Exporting sounds on 64kbps will occasionally create an awful screeching noise. It seems to be totally random, and platform specific. We just avoid 64kbps nowadays [whenever our sound designers let us].
We really like Fuel’s Library Items panel (download here). It allows for simple batch optimizing of library items…though there is a typo in the sound tab which will force you to dig around for the JSFL and string replace “kpbs” with “kbps.”
Complex Vectors and Text
You’re forcing Flash to redraw complex vectors every frame. Same with text. With dynamic text fields, put ‘em in a movieclip and set them to cacheAsBitmap. If you ever need to animate them you can unset the cacheAsBitmap, but if they’re just standing in place there’s no reason they should tax the processor.
Don’t Break Apart JPGs
The ultimate no-no. The player has to look at the JPG every frame and figure out what to draw. This is such a waste. It’s even worse if you pull a complex shape out of the JPG…now you’re forcing Flash to do double work. If you need just a portion of a JPG, use a mask…or better yet, bring it back to Photoshop and crop the piece you need.
No Looping in onEnterFrame
Running a for loop every frame is actually slower than directly defining what you need. If you have a small amount of data you need to increment, consider hard coding it.
// slow
var a = [0,1,2];
mySlow.onEnterFrame = function():Void
{
for(var i=0;i<a.length;i++) { a[i]++; }
};
// fast
var a = [0,1,2];
myFast.onEnterFrame = function():Void
{
a[0]++;
a[1]++;
a[2]++;
}
…in fact, we can take this a step further.
Avoid All Excess Looping
Let’s pretend you have a complex set of data. Storing them in as arrays sounds like the easiest battle plan, right? Wrong. It can lead to trouble. Consider this very basic example:
var error_array = [];
error_array[0] = [404,"File Not Found"];
error_array[1] = [500,"Misconfiguration"];
error_array[2] = [403,"Forbidden"];
error_array[3] = [200,"Okay"];
// assumes an outside event has fired that calls this function
onNewError($error_code:Number):Void
{
for(var i=0;i<error_array.length;i++)
{
if(error_array[i][0] == $error_code)
{
trace("Error Found!: " + i);
break;
}
}
};
function doesErrorExist($error_code:Number):Boolean
{
for(var i=0;i<error_array.length;i++)
{
if(error_array[i][0] == $error_code) return true;
}
return false;
};
In this case, its just a few items…but every time onNewError or doesErrorExist is fired, we have to loop through our entire array to find the corresponding error (if at all). There’s a better, more organized way to do this same task:
var error_obj = {};
error_obj["e_404"] = "File Not Found";
error_obj["e_500"] = "Misconfiguration";
error_obj["e_403"] = "Forbidden";
error_obj["e_200"] = "Okay";
onNewError($error_code:Number):Void
{
trace("Error Found!: " error_obj["e_" + $error_code.toString()]);
};
function doesErrorExist($code:Number):Boolean
{
return error_obj["e_" + $error_code.toString()] != null;
}
By using an object to create an index, we’re able to bypass any looping. There are times when using arrays of objects (objects populated with arrays) can be useful, too. The idea is to avoid any looping.
Avoid Non-Frame Specific Events
onMouseMove, Stage.onResize are prime examples of functions you should try to avoid doing any sort of extensive programming in. Since they fire independent of the frame rate, its plausible your user gets 5 or 10 or more Mouse Move events before the frame updates once. We find an onEnterFrame detecting mouse position works far faster.
Don’t Play Clips Backwards
Sure, it sounds like a great idea in theory, but there are serious negatives.
For starters, you need to be aware of any potential scripting on your timeline as you go backwards. You’ll need to know whether you need to ignore these events or not, which means before you know it you’ll be rewriting the Timeline entirely in ActionScript.
Second, you’re making the Player do double work. It already has to look to see what to refresh x number of frames per second…now you’re making it run code to move a timeline backwards every frame on top of that.
If your timeline is complex and you’re trying to play it backwards, you’re going to run into bugs. And if it’s simple, you should probably take the 30 seconds to keyframe it out the opposite way and just gotoAndPlay a different label. The Player (and your end users) will love you for it.
Filters are nicest on multiples o’ 2
When using a Blur Filer, use multiples of 2. The player seems to like this better. We haven’t done any specific benchmarks on this…but it seems like it can’t hurt to be safe. :)
Kill! Destroy! Dispose! Whatever.
This one is especially pertinent for AS3, given that garbage collection is far stricter than in previous versions of Flash, but it’s a good practice just the same. In every class we write, we include the following function:
function kill():Void {};
kill will wipe out any onEnterFrames, clear any intervals, remove references to movieclips, reset or null all variables, remove any references to objects, and on and on. The idea is that the only way to insure garbage collection is to make sure we’ve cleaned up our mess in the first place. We’ll know for sure that we’re not storing duplicate classes in memory. Flash will be oh so happy.
Check out Grant Skinner’s AS3 Resource Management series for more information on garbage collection, and his awesome Janitor class to help expedite your transition.
Advanced Collision Detection
hitTest is an acceptable way of detecting small collisions between instances, but there are alternatives out there that are custom built to perform much better. Here are two links to some advanced detection algorithm’s we’ve looked into before. With Recursive Dimension Clustering, the screen is divided into regions and then detection is done based on what elements fall into what regions. It’s pretty nifty and fast, and you can wow all of the babes with your newfound math babble. Another method is Grant Skinner’s Shape Based Hit Detection, which uses BitmapData brilliantly to draw a comparison of two objects and return the bounds where they intersect (if at all).
You can pull off a custom collision detection with hitTest simply by placing empty movieclips around a complex shape and hitTesting those instead of the shape itself. Better for the player to do several hitTests on simple objects than one hitTest on a complex one.
February 28th, 2007 by Stephen / 28 Comments



