AS3: MovieClip.addFrameScript
MovieClip.addFrameScript is a very powerful way to decouple script from the timeline. This post help you understand how it works.
When movies are compiled (including the top level swf), if no class is provided. Flash creates one for you behind the scenes. 1 Class is created per 1 MovieClip, typically sharing the same name. It helps to understand how the conversion happens, to make best use of it, and writing code on the timeline in general.
- imports are only needed once in a timeline
- variables declared on a frame, end up as class variables,
- you can't redeclare variables on multiple frames.
- only one function is called per frame, this can be tweaked, overridden or amended with addFrameScript
You can't redeclare variables,
on frame1 and frame2 put:
var aName:String = "A";
will give you the following error:
1151: A conflict exists with definition aFrame in namespace internal.
however putting on Frame 1
var aName:String ="A"; //frame1
and on Frame2
aName = "B" //frame2
works fine.
MovieClip.addFrameScript
Is the undocumented glue between the timeline frame and the class's function ( if bound). There can only be one script/function bound to a frame using the built in addFrameScript, though an alternative approach is to poll currentFrame changing (via Event.ENTER_FRAME).
This binding happens via addFrameScript(frameNumber-1, functionToCall...); Note that the first argument is ZERO BASED (while normally the timeline is 1 based)
During the Fla.compilation our script gets put into a function like:
Class Test_fla{
function frame1():*{
var aName:String = "A";
}
function frame2():*{
aName = "B";
}
}
The important thing to note is the frame1...frameN naming, where N is the frame number 1-whatever, We can validate this by decompiling the swf, using the AbcDump utility, which takes our test.swf and outputs:
function Test_fla::frame1():* /* disp_id 0*/
{
// local_count=1 max_scope=1 max_stack=2 code_len=16
0 getlocal0
1 pushscope
2 findpropstrict trace
4 pushstring "onFrame1"
6 callpropvoid trace (1)
9 findproperty aFrame
11 pushstring "aFrame"
13 initproperty aFrame
15 returnvoid
}
So the end will be a frame to function mapping (which we can't see) like:
- [0]= frame1()
- [1]= frame2()
Note again the frameNumber is zero based, while the script is 1 based.
Consequences
which has 2 consequences,
- you can't name your own functions frameN if one already exists. This is the same error as compiling in the same *.as:
function frame1(){}
function frame1(){
}
- you can call and over ride these functions,
addFrameScript(0, overrideFrame1);
function overrideFrame1():*{//do something before
frame1();
//do something after
}
In addition the top level Class, will be shared across different scenes, but these can have on the timeline different configuration values (say which language to use), and when used with jsfl to publish all scenes, create a handful of starter files.
Conclusion
This is kick ass for completely separating a UI.swf from a Controller.swf Keeping designers working on the timeline, and coders out of the design.
For the coders, and others working in Flash, is the ability to use addFrameScript to perform actions when a frame is fully initialized and children are on stage, similar to onLoad() in AS2, where-as use of ENTER_FRAME gets called prior to children in that movieclip getting called...this is similar to onInit() in AS2. So for example you can have a button only on a particular frame and setup it's event listener to call your controller...in another swf, regardless of what the playhead is doing.
In someways I find this cleaner than watching using addedToStage, rendered etc.
REFERENCES:
[1] Senoculars post
[2] FlashGurus info as Senocular
[3] BIG SPACESHIP, Extending addFrameScript to use the frame labels instead of framenumbers... the same glue I'm using as well, though I'm more interested in events being generated, than script.
[4] an alternate approach on evilzug's journal using ENTER_FRAME
You should be aware if using that approach that when the framescript is called via the ENTER_FRAME, items in the frame may have not been added yet, so this approach can't be used to bind to objects on the timeline (e.g. to registerListeners to buttons), this the addFrameScript does not suffer from.
Here's an output with one on the timeline the other added via oizys's frameScript approach:
entered frame 3
onReachedInstructions instructions null <- no button yet, via modified addFrameScript
timeline reached instructions [object SimpleButton] <- normal addFrameScript
onReachedInstructions instructions [object SimpleButton] <- now the button exists
The Great Divide

Although just one number has changed from Flash8 to Flash9, so much has changed, they might as well be on separate continents. The canyon is the best metaphor I've found to help project managers, programmers and the like, understand the differences in upgrading, and communication gap in them talking to each other, as well as the difficulties if Flash8 and prior projects are continued to be invested in, how much harder it will be in the future. The paradigms are that different, and the rate of development at Adobe is increasing faster, so
in the future it may be even harder to get up to speed.
AVM2Loader Converting AVM1 to AVM2
I had the need to load up Flash7 swfs no script in an AS3.0+Flash9 application. Without the ability to edit the swf to say use LocalConnection as other sites suggest, as they are output from a video2swf convertor.
There was some hope of converting byteCode since the file is so simple, and the raw data isn't the same. I discovered AVM2Loader through ActionScript Classes, which got it from Fladdict who apparently was linkiing to the file on a snippets site with Trac, and that is/was down or no longer working. So I'm copying it here incase others need to find it.
While it helps, In testing it's very limited, which is true in general of AVM1Movie. I wanted to load Flash7 files as output by Turbine that contain PCM audio and no graphics for use with an AS3/Flash9 based UI. Sadly no dice. With or without AVM2Loader, the audio clip stilll shows up as an AVM1Movie and can be loaded/replayed 2 times before it stops working, despite all loading events being sent. This latter seems like a bug, as no matter what I try once it's loaded it won't play reliably by loading it again, trying to remove it prior gives caller must be child of Display object or something.
AVM2Loader does work with simple graphics, put a tween on stage...they appear as MovieClip in the AVM2 so can be added to the displayList and controlled with play(), stop() etc, but somehow audio packets throw off the translation. In testing it bypasses one of the 'hackA' segment, and the byteHeader is different, changing it to to match didn't help.
I wish I knew more about the swf file format(s). Working in hex is painful for me, feels like I'm starting a tire treads when I should be driving.
AVM2Loader.as
package {
import flash.display.Loader;
import flash.events.*;
import flash.net.*;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import flash.utils.Endian;/**
* Loads both of AVM1 and AVM2 swf as AVM2.
*/
public class AVM2Loader extends Loader {
private var _urlLoader:URLLoader;
private var _context:LoaderContext;/**
* loads both of AVM1 and AVM2 movie as AVM2 movie.
*/
override public function load(request:URLRequest,context:LoaderContext=null):void {
_context=context;_urlLoader=new URLLoader ;
_urlLoader.dataFormat=URLLoaderDataFormat.BINARY;
_urlLoader.addEventListener(Event.COMPLETE,_binaryLoaded,false,0,true);
_urlLoader.addEventListener(IOErrorEvent.IO_ERROR,_transferEvent,false,0,true);
_urlLoader.addEventListener(ProgressEvent.PROGRESS,_transferEvent,false,0,true);
_urlLoader.addEventListener(Event.OPEN,_transferEvent,false,0,true);
_urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS,_transferEvent,false,0,true);
_urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,_transferEvent,false,0,true);
_urlLoader.load(request);
}
private function _binaryLoaded(e:Event):void {
loadBytes(ByteArray(_urlLoader.data),_context);
_urlLoader=null;
}
private function _transferEvent(e:Event):void {
dispatchEvent(e);
}
/**
* loads both of AVM1 and AVM2 movie as AVM2 movie.
*/
override public function loadBytes(bytes:ByteArray,context:LoaderContext=null):void {
//uncompress if compressedbytes.endian=Endian.LITTLE_ENDIAN;
trace("loadBytes" + bytes[0] );
if (bytes[0] == 0x43) {
//trace(" hackA");
//many thanks for be-interactive.org
var compressedBytes:ByteArray=new ByteArray() ;
compressedBytes.writeBytes(bytes,8);
compressedBytes.uncompress();bytes.length=8;
bytes.position=8;
bytes.writeBytes(compressedBytes);
compressedBytes.length=0;//flag uncompressed
bytes[0]=0x46;
}
hackBytes(bytes);
super.loadBytes(bytes,context);
}
//if bytes are AVM1 movie, hack it!
private function hackBytes(bytes:ByteArray):void {
//trace(" hackB");
if (bytes[4] < 0x09) {
trace("hack 9");
bytes[4]=0x09;
}
//trace(" hackC");
//dirty dirty
var imax:int=Math.min(bytes.length,100);
for (var i:int=23; i < imax; i++) {
if (bytes[i - 2] == 0x44 && bytes[i - 1] == 0x11) {
bytes[i]=bytes[i] | 0x08;
return;
}
}
}
}
}
TEST CODE
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.display.SimpleButton;
import AVM2Loader;var avm1:AVM1Movie;
function testAudio(e:Event = null):void {
trace("testAudio");
//var request:URLRequest = new URLRequest("video.swf");
var request:URLRequest = new URLRequest("Flash8Movie.swf");
var ldr:Loader;
if (true) {
trace("start AVM2Loader");
ldr = new AVM2Loader();
} else {
ldr = new Loader();
}
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoaderComplete);
ldr.load(request);
}function _onLoaderComplete(event:Event):void {
trace(" _onLoaderComplete");
var info:LoaderInfo = event.target as LoaderInfo;
trace(" _onLoaderComplete " + info.content);
if (info.content is AVM1Movie) {
avm1 = AVM1Movie(info.content);
} else {
addChild(info.content);
MovieClip(info.content).stop();
var dO:DisplayObject = DisplayObject(info.content);
}}
stage.addEventListener(MouseEvent.CLICK, testAudio);
testAudio();
Sketch: View Controller separation, MovieClip.addFrameScript
There are several approaches to using Flash assets in Flex, as assets. Predominately centered around using getDefinitionByName(), e.g. here we are going to pull from a library of 5 possible graphics and attach it to stage.
var dc:String ="missle"+num+"_MC";
var classRef:Class = getDefinitionByName(dc) as Class
var mcMissleGraphic:* = new classRef(); //might be a movieclip, shape or sprite)
addChild(mc); //similar to attachMovie of old
or extending this example to make it a user-defined class, by passing it into the constructor (using composition)
var missleMC:Sprite = new Missle(mcGraphic);
addChild(missleMC);
In this example the visual tree will be
- Child
- MissleController (has a graphic)
- MissleGraphic
- FlameAnimation
- BlinkingLight
- RedBlinkClip
- GreenBlinkClip
The downside is that if something ever needs to access the MissleGraphic or child clips (like hiding red or green blink clips) it must go through an additional layer. When coding it's easy to get these out of synch, and the layer is one more thing to keep track off. It's the difference between IS A and HAS A, and also doesn't faciliate timeline control of the user experience.
But it's a useful approach, and Grant Skinner even has a library to help deal with the loading of external swfs. That must be fully initialized prior to attempting this.
http://www.gskinner.com/blog/archives/2007/03/using_flash_sym.html
and if your going to be embedding it you can also follow this
http://www.digitalflipbook.com/archives/2007/03/associating_cus.php
Sketch's approach Designers First, Script later
Sketch differs from this as these all imply the script has control first and is pulling from the library of assets designed in Flash IDE. Sketch prefers that designers can lay things out on stage, working on the timeline to process, and then later load up script and have it embue them with life, primarily as placing and sizing things laying out things is best for visual tools, when outside of grid layout controls for data driven components. This is not to say that you can't have AS code generate all manner of things, just that most approaches don't give you the option for the first. In the mini-game industry, and animated content this is far more acceptable to smooth production and good users experiences.
UI and Controller synchronization via MovieClip.addFrameScript
So I was looking through the old paperwork when I was a part of the Flash8/9 beta. Since it's been released, and out for awhile it's interesting to compare what made it where and what's changed. It's also much easier to read having gotten up to speed on AS3. One of the things I've rediscovered is MovieClip.addFrameScript, which totally fits into the Sketch way separating UI from controller synchronization, though I hadn't seen it before. Of course I wasn't the first to discover this:
http://www.flashguru.co.uk/undocumented-actionscript-3/
http://www.kirupa.com/forum/showthread.php?p=2098268
BigSpaceShip in particular is on a similar approach, he has an interesting take of introspecting the movieClip labels and attaching a script to when they are fired. This fits into the Sketch philosophy of using minimal or no code in the view to indicate state.
http://labs.bigspaceship.com/blog/?p=43
Though Sketch again differs in it's approach, while it too introspects the framelables, creates a registry of sorts, it treats changes of the playhead, as an event to broadcast so more than one party can listen, addFrameScript is like removing that intermediate event and directly calling a script instead. The downside to the addFrameScript approach is it will override any script inside inside the timeline, so in some ways polling based on EnterFrame events is less prone to breaking something the designer is doing (e.g. play/stop or configuration);
Generate UI Class Linkage via ByteArray?
The other cool thing I found which is a lower level hack is the Tag in byteCode that the Flash Player that binds the library symbol with the actionscript class, in a streaming fashion so that not everything has to be put on frame1. Looking through the source code documentation it's still in, and seems similar to the Object.registerClass.
I'm unsure if the security model will permit a loaded swf to append to the parent, but it seems likely given we are already doing that with getClassDefinition etc.. It's also possible that something similar may be done via getClassDefinition, if however the linking mechanism in the player can be called. This would be a huge advantage to Sketch as currently the conversion of instances one stage to classes is via addChild/removeChild on the displayList. Not horrible but not clean, in particular keyframes can break some things as a new reference will be on stage but if it keeps the same name the displayList doesn't get notified.
Ideally this can be via ByteArray it's possible to read and write swfs at a binary level. So generation of the linkage might be possible via AS3, without the need for composition.
In Object.registerClass, an library item IS a user defined class. With AS3, it's similar but the linking is defined at compile time in the IDE, thus we have to use composition instead for late/lazy view binding. So Object class HAS a view. It's possible to use Proxy to make this more seamless but then we lose the typing of MovieClip/Sprite. e.g. a Ball clip is a ball, is also a MovieClip.
Some work on dumping the tags is already being done, so it might not be too hard to find it out.
http://www.5etdemi.com/blog/archives/2007/01/as3-decompiler/
http://joeberkovitz.com/blog/2007/04/08/secret-life-of-swfs/
AS2 to AS3 Conversion Annoyances
After using FDT and MTASC both with strict compilation, I was thinking that converting to AS3 would be straightfoward. Alas.. no.
Some of it is syntax, but frequently in the pursuit of performance or architectural benefits, I had to take advantage of tricks or in the language, and many of these hacks are breaking, or rather even some of the core features no longer work on the AS3 architecture.
Nullable types bye..
For all the type strict typing in AS3, one of the biggest annoyances is the removal of nullable types. This has had profound impacts on migrating code.
what used to be
public function doSomething(var aNum:Number):void{
if(aNum == null){
aNum = DEFAULT_NUM
}
}
Now I have to use  aNum:* which makes it harder to read, and now I have to introduce argument type checking in the method body, quickly adding to the size. Grr. What's silly is you can still create without initializing vars e.g.
var numA:Number;
//sometime later, what is numA before this?
numA = -1;
in addition, var obj:Object = numA is a legal call. So.
I'd be happy if they make a separate nullable type e..g NullableNumber that extends Number and that NullableNumber is itself finalized.
Initialization Â
The intialization into the function signatures is neat and increases readability, but totally flummoxes collections when your defaulting to the length of the array, here again we have to use the wildcard, which defeats type checking during compilation.
For in...GoodbyeÂ
For in was a useful tool to introspect values and interate over collections. Of course since this uses the prototype chain which is slow it's been relagated to only looking at the prototype chain, as most the variables and method have been moved to the fixed class inheritance, many uses of introspection have been broken, all over the place.
