Why AMF Rocks and beats XML
Ted made a great summary of why AMF is Flash's file format of choice.
http://www.onflex.org/ted/2007/11/abcs-of-amf.php
While I've touted the benefits about persistence and serialization of native classes in previous posts (see persistance or serialization categories). Often XML is preferred for data interchange with servers or text editor compatible configuration. Here Ted had a gem in the comments, comparing why using XML inside of AMF over XML in a text file, rocks!
E4X is super handy for querying the data and returning collections after parsing to xml. That said XML is a native object and can be included in AMF3. The data is stored as a string zlib compressed and when deserialized becomes an XML object again.
bytes.writeBytes( new XML('') );
This gives you zlib compression and benefits of e4x in one go. zlib is very effective on xml and can compress documents as much as 20X, I have seen 300Kb XML files become 7K given tag redundency in the ASCII text.
The XML human readable is important as often for games and such that I build, but frequently it's doesn't make sense to build a configurator given the many excellent XML editors out there. In addition it's very easy for most servers to generate, where they can't generate AMF as easily. But there are two compelling benefits to going that route:
1) As the XML grows, compressing things by up to 20x can improve the responsiveness of the application (like startup of a website), and reduce network costs.
2) XML stored this way is native data type to Flash not a string...meaning it's already an XML accessible to E4X instead of a random string that has to be parsed into XMLNodes. Reconstituting the data out of the AMF stream still takes overhead, but it should be much faster than parsing it from interpreting tags and attributes from the plain text, as most the marshalling code is in C inside the player instead of actionscript. Read better experience for end users.
What occurs to me now is that either a very simple AIR app act in a similar fashion to Winzip for compressing/decompressing text for consumption by flash, or act as a simple text editor, just using a text field.
I'm sure that Adobe will open up the AMF API so that servers can generate raw AMF without using remoting or RMTP. This will open up the possibilities for CMS driven websites to prerender sites daily out of database requests.
Using ByteArray to Serialize AS3 Classes for AIR, Web and Everywhere!
When using AIR's FileStream to write AS3 classes to ByteArray, I've found that the same file format works for loading in via URLLoader, as well as what's uploaded using FTP. This isn't surprising, but wanted to test anyway.
What this means is that ByteArray is a groovy ubiquitous format for storing full classes and dependencies and references, via ByteArray.writeObject and ByteArray.readObject (provided registerClass is used), without nececessarily having to parse them manually though that is still suggested for versioning and class evolution. Meaning you can create a file using AIR , send that file to a friend in email, and have them open it up in a flash based webpage, and they operating in a webpage could Http POST the change back to you. The ByteArray can have rich complexity, classes, self references, collections, etc. This could be used to save whole game states (even massive multiplayer, NPC brains, etc). Or in my target case debugging outside other non-AIR runtimes to speed development.
How to save to the same directory the swf AIR is running from
This took me a bit to figure out. Primarily as I'm using AIR to write the files in Flash CS3 and it's not immediately apparent in the docs, how to target the 'self' directory of the swf. This isn't recommended in the docs (which I agree) but in the case I'm using it for it makes sense to do so. The API for AIR is here
-
-
// this is in url path
-
var file:File = File.applicationDirectory.resolvePath( "example.jpg" );
-
trace("file " + file.url + " " +file.nativePath ); //outputs app:/example.jpg, and "C:\SomeDirectory\Somepath\example.jpg"
-
-
var wr:File = new File(file.nativePath);
-
// Create a stream and open the file for asynchronous reading
-
var stream:FileStream = new FileStream();
-
stream.open( wr, FileMode.WRITE );
-
stream.write.....
-
stream.close();
-
This would be swf._url in AS2, or in AS3 DisplayObject.loaderInfo.url , as when using relative links in URLLoader to load whatever you're saving this will resolve properly in AIR. Turns out flash.filesystem.File.applicationDirectory is the droid you are looking for. It's not fully qualified (e.g. file:C:\somedirectory...) when traced it looks like:
"app-resource:/yourSwfNameGoesHere.swf"
unless you use the file.nativePath which is required in order to actually write to the directory. So you can use still this in old school string name parsing the swf name out, so you don't have to use File.applicationResourceDirectory if your looking to make a single swf for both AIR and Web use, and on the web, the AIR libraries won't be accessible.
Here's two handy scripts to help inspect ByteArray's, the first looks at the 1's an 0's, the other looks at the objects in the stream, using the getQualifiedClassName, and if it finds an Array or sub ByteArray attempts to inspect it.
/* converts byteArray to binary like
TraceBits--------------------
0 = 110
1 = 1011
2 = 1001000
3 = 1100101
*/
-
-
function traceBits(bA:ByteArray) {
-
trace("TraceBits--------------------");
-
for (var i:int = 0; i < bA.length; i++) {
-
bA.position = i;
-
-
var inte:uint = bA.readByte();
-
trace(i+ " = " + inte.toString(2));
-
}
-
}
-
-
function traceByteArray(bA:ByteArray) {
-
var o;
-
var cnt:int = 0;
-
var cnm:String;
-
-
var o2;
-
var cnt2:int = 0;
-
var cnm2:String;
-
try {
-
while (true) {
-
o = bA.readObject();
-
cnm = getQualifiedClassName(o);
-
trace( cnt++ + " " + o + " : " + cnm);
-
if (cnm == "flash.utils::ByteArray") {
-
try {
-
cnt2 = 0;
-
cnm2 = "";
-
while (true) {
-
o2 = o.readObject();
-
trace(" " + cnt2++ + " " + o2 + " : " + getQualifiedClassName(o2));
-
-
}
-
} catch (err:Error) {
-
}
-
} else if (cnm == "Array") {
-
trace("array contents...");
-
try {
-
var ar:Array = o as Array;
-
trace("ar " + ar);
-
trace(ar.join("\r"));
-
} catch (err:Error) {
-
trace(err.toString());
-
}
-
}
-
}
-
} catch (err:Error) {
-
}
-
}
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
AS3 Serialization and Deserialization: ByteArray BitmapData registerClassAlias
Grr. Flash's registerClassAlias does not work with BitmapData (I wonder ?), so while you can save it to the ByteArray and wherever that can go... you can't get it back! As BitmapData complains about the constructor getting no args, presumeably that memory allocation is fixed upon the constructor.
This means you'll have to roll your own, which isn't too hard but it when having the BitmapData as members of other classes it might be a pain as each will have to serialize/deserialize the BitmapData using the IExternalizable.
Another option might have been overridding, but while BitmapData can be extended there isn't a no arg constructor, so the constructor can't really be overridden efficetively. A Proxy could be created for this.
Andrew has a good workaround, iterating over the pixels, though I've opted to write the width and height into the stream as well. You can use writeObject for the height and widht without problems (versus writing writeInt)
For additional details and others experiencing this problem: search terms "registerClassAlias BitmapData" "deserialize Bitmapdata"
- http://www.google.com/search?q=deserialize+BitmapData&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a
- http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?catid=641&threadid=1255888#4547075
- http://osflash.org/pipermail/red5_osflash.org/2007-April/011091.html
Solutions
- [1] http://www.cynergysystems.com/blogs/blogs/andrew.trice/BitmapData/SharedDesktop/srcview/
- [2] http://www.barncar.com/writejpg.mxml (sample code using corelib to save as Jpg)
- http://www.bytearray.org/?p=90 (a more flexible snapshot of video/swf to jpg using corelib, which is a completely different solution path)
- corelib is here http://code.google.com/p/as3corelib/
NOTE:
if you do need to use registerClassAlias you can do this trick:
private static const REG:* = registerClassAlias("reflection.IntrospectableObj",IntrospectableObj);
Introduction to ActiveFrame, Cogs, Chain, Score, Sketch, fXperience
Intro to StateMachines
![]()
The good news is if your in team development, you've been diagramming them for years, as flowcharts and bubble diagrams. Bubbles are states, arrows are transitions. This is one of the keys to their usefulness in teams. The diagrams can be understood and modified by anybody, and can represent anything from a users interaction with the whole application, a game's AI, or the graphical transitions in a button rollover. Any Animator who has done keyframes and tweens knows has been unwittingly using states and transitions, but what about developers?
Most any application developer is familiar with OOP, and many design patterns like the State Pattern. Some may have heard of the term finite state machine (FSM) with a possible vague understanding of what one is/does, most are probably not familiar with Hierarchical state machines (HSM), which are especially powerful, especially in experience driven, Flash
and AIR content working with asynchorous network requests, and large media upload/downloads, long timeline, all of which have complicated life cycles with asynchronous call/callback.
I started using them in projects after first learning about them in software from Jonathan Kaye. After using a few different versions in a few different projects, and developing my own, I have been on a quest to spread the word ever since. OOP and SM's foundational aspects. They are orthogonal and complimentary, OOP describes the nouns of the system StateMachines's the behavior over time. An example the cat is meowing, cat is the noun, meowing is the current state, and it will change over time.
Intro to Cogs
Cogs is the AS3 library of statemachines, and common patterns that people can extend/plugin similar to how people reuse design patterns, to avoid reinventing the wheel. Just as in Design patterns, there are fundamental patterns, from simple toggleable button, to synchronized media players.
It's named Cogs first after the tiny teeth that compose gears, that when meshing properly make up engines which power most the mechanical world, and secondly Cognition as the meshing of neurons make up the engines of intelligence. Cogs primary use is increasing the intelligence of components, games, game AI, and applications, by several approaches:
Cogs allows you to ask where you are, where you can go, and automate chains of execution between two states. Often event driven components are black box event generators, identical events broadcast at different times mean completely different things (like NetConnection) this is far from simple.
The FSM and HSM are based on Miro Samek's approach of using functions instead of classes to represent states, this very low level implementation has several advantages, one of the first is being less code to understand and write. It also enables behavioral inheritance, similar to. Cogs is different in several ways from Miro's inspiring work in his book. First all possible transitions are supported instead of just the common ones, and are validated by the unit tests..and the testing framework itself being an FSM and supporting Asynchronous testing easily.
Ongoing I'm working on API's for deep history, serialization/deserialization a graphical visualizer, among other things.
ActiveFrame
Cogs is part/foundation of a larger framework named ActiveFrame. Which strives reduce commmon architectural decays, largely by decoupling.
- A message bus enabling event decoupling,
- Java Spring 'Dependency Injection' for Implementor Interface decoupling,
- plus a decoupling of the UI and controller..which can live in completely different swfs, similar to AS2.0's Object registerClass.
It's reasonably small for all these features, allowing it to be used for components, games, game AI or full blown applications.Sketch
is the visual extension of the Cogs, to deal with the View/UI world (e.g. movieclips/timelines, sprites), common layout, and UI/controller decoupling communication. While Sketch can be used whereever Flash can go, it's strong points are design and script heavy projects, in teams that have strong separations between design and scripting... they don't even have to be working on the same file.
Since it's based on Cogs, it's very easy to prototype and if flow diagrams (state diagrams) are used in the IA and Creative phases, it can act as a common language between design, IA, and engineering, as diagrams can easily be translated to code and back, and reworked easily. It supports what I call "introspectable UI's", in that the statemachine can act as the model, and summarize all the active states supported options to generate an appropriate "where can I go from here" UI.
Chain
is the workflow component, it's coordinates dependent parallel and sequential and nested tasks, with asynchrous call/callbacks, providing status along the way. Some uses for this are preloading assets, playlists, unit testing, easing and tweens, build out and tear down of UI's.
Score
is the the parallel timeline orchestration component, similar to a Orchestral score for coordinating multiple instances with a single virtual playhead. Useful for overlapping events. It's similar to an actionscript version of the Flash IDE timeline.
fXperience
is the visual components library. This is based off Cogs/Sketch, components have a wider lifecycle than those built into Flash and Flex., e.g. a button transitioning in, transitioning to up, transitioning to over etc. They support easy and iteratible skinning, so projects can start out simply as a graphic than graduate to more complicated ones as need requires.
Misc
All are a part of the TroyWorks AS3 code library that is MIT licensed on google (but isn't finished/uploaded yet, API isn't stable) There are many other useful utilities, datastructures and components of interest I'll be covering in more detail as they get more fully flushed out and converted to AS3 from AS2, I'm trying to make sure I don't add something already in AS3, and take full advantage of AS3's architecture.
