Saturday, April 16, 2011

Using AS2 onEnterFrame to speed the display of MovieClips

ActionScript 2 broadcasts the onEnterFrame event regularly. If a movie is running at 24 frames per second, you would expect to see it every 1000/24 milliseconds, or about every 42 ms. By using traces like

trace("starting frame at "+(new Date()).getTime());

you can actually measure the passage of time.

It turns out that like many of our 7.5 hour work days, a frame actually can last a lot longer than its appointed 42 ms, if there is a lot of work to get finished. Recently, I have been working on a tool that graphs skips on a number line. Operations are menu-driven. The tool currently has about 4000 lines of code, the menu another 1600 and a variety of other classes are used to create buttons, text, stepper widgets etc. In all, it takes about 2500 ms to display. If all of that code executes on one frame, the wait is agonizing. Nothing is displayed until all the code is executed.

There are ways to tell Flash that it can pause and update the screen. One is to break up the code into functions that are called with onEnterFrame.

private function step1():Void{
   this.createEmptyMovieClip("timer", this.getNextHighestDepth());

   this.timer.onEnterFrame = function(){
private function step2():Void{

   this.timer.onEnterFrame = function(){
private function step3():Void{
   delete this.timer.onEnterFrame;

and another is to use setTimeout, which unfortunately was left out of the list of keywords for AS classes and must be envoked in a strange way.

_global["setTimeout"](this, "step2", 0);

In my tests, I could not see that either approach was better and stuck with the onEnterFrame approach, since it was the one I tried second. Now, I see a container on the screen as soon as step 1's code is done, after about 40 ms. I see the number line and some options next and finally I see the menu appear. The illusion of progress is very satisfying compared to waiting the full 2500 ms for the container to appear. The screen doesn't actually change after about 900 ms, since a lot of the work is to set up subMenus and configuration panels that are not immediately visible. Each of these frames take significantly longer than 42ms to perform, but giving pause after each visible change provides a much better result.

Generally, it is a pain for a class to take up more than one frame's worth of code, since the programmer would have to wait to customize or work with the instance. This is mitigated by having the class broadcast an event when it is all done.

this.broadcastMessage("onSkipCountingToolReady", this);

The program can listen for the event and then perform subsequent operations, without having to guess how many frames or how many milliseconds to wait before it is safe.