Two days and a night with Actionscript
by Sebastien Mirolo on Sun, 20 Feb 2011Lot of buzz is going on in the flash vs. html5 debate. Nothing is better than a hands-on experience so I figured I will develop a simple app to find out for myself. I have experience building user interfaces in Java and different C++ frameworks, I also have done my fair share of html and server-side cgi coding but it the first time I looked at any flash related source code.
First few searches through google pop out which seems a very good tutorial with full working code examples. It turned out after a couple days of searches that it is very easy to find actionscript snippets but to actually get a source package that can be downloaded and compiled is definitely not. As a result, the code in is producing a bunch of deprecated warnings on the latest 4.1 SDK but remains the best tutorial I have come across so far.
First things first, it is time to download the SDK and install the different plug-ins necessary for development of my first actionscript application. I downloaded the free 4.1 SDK (flex_sdk_4.1.zip) from Adobe website and the flash browser debug plug-in for OSX (flashplayer_10_plugin_debug_ub.dmg). There is a flash debug plug-in bundled with the SDK but it is older. The flash SDK is written in Java and comes as a bunch of .jar files and shell script. I copied the content of the bin, lib and frameworks directory to /usr/local alongside other compilers rather than messing around with my PATH environment variable. The flex_sdk_4.1.zip file is huge but the components actually required to do development have a much more manageable size.
binDarwins := aasdoc acompc adl adt amxmlc asdoc compc copylocale \ digest fcsh fdb fontswf jvm.config mxmlc optimizer \ swcdepends swfdump tar jxf $(resourcesDir)/srcs/flex_sdk_$(version).tar.bz2 $(installDirs) $(binDir) $(installScripts) $(addprefix flex_sdk_$(version)/bin/,$(binsDarwin)) $(binDir) $(installDirs) $(libDir) cp -rf flex_sdk_$(version)/lib/* $(libDir) $(installDirs) $(installTop)/frameworks cp -rf flex_sdk_$(version)/frameworks/* $(installTop)/frameworks
I have started to use Chrome to test my little application. Since Chrome comes bundled with a version of the flash plug-in but not the debug version, I had to disable it in order to run the system version I just installed.
Enter chrome://plugins as the url Click the top-right "Details" Click "Disable" for Flash whose Location field is inside the Chrome application directory.
I am using Emacs to write code and many other text documents (this blog post included) so I downloaded and installed and Emacs ActionScript Mode.
$ cp actionscript-mode-haas-5.6.el \ /Applications/Emacs.app/Contents/Resources/site-lisp/actionscript-mode.el $ diff -u ~/.emacs.prev ~/.emacs + (autoload 'actionscript-mode "actionscript-mode" nil t) + (add-to-list 'auto-mode-alist '("\\.as\\'" . actionscript-mode))
I now only had to figure out how to debug Actionscript code before diving into some real coding and that took me a major part of the night. There are a lot of Actionscript libraries, plug-ins and extensions but none I could reliably get to work out-of-the-box. Firebug console log, ThunderBolt, Firebug console for flash and flashfirebug, all seem great and after dealing with different Firefox/Firebug version incompatibilities I got the interface up and running but never actual debugging information showing up while running my code.
As I learned more about the issues, it looks like all the solutions are at the base built on top of Actionscript trace statements and the flashlog.txt output file. So I decided to get at least this working and that took a major part of the night. In the end, the following article was insight full. Here what needs to be done:
# 1. Locate mm.cfg and flashlog.txt $ find ~ -name 'flashlog.txt' $ find ~ -name 'mm.cfg' # 2. Add the following lines to mm.cfg +TraceOutputFileEnable=1 +ErrorReportingEnable=1 +MaxWarnings=0 # 3. Compile your code with "-debug=true" $ mxmlc -debug=true -output $@ $<
Voila! By the time I went to bed, I was finally able to do basic debugging of my actionscript code. Time to get on the actual coding part. I just added one last bookmark to Actionscript 3.0 Reference for morning reading.
Loading an external file
The scope of my little application was to load an external vector graphic file and display it in a button. Dragging and dropping the button onto a main canvas creates a new instance of the button graphic like most diagram tools (think Omnigraffle).
There at a lot of SVG vector image files at clker so I got one I like to test my code. As it turns out you can embed svg in your compiled flash (.swf) with code like:
[Embed(source="../data/baby_chick.svg")] public static var BabyChick:Class; ... var d:DisplayObject = DisplayObject(new BabyChick());
First you will notice a warning "The use of SVG has been deprecated since Flex 4. Please use FXG" from the flex compiler. After a few more google searches, I stumble upon FXG 1.0 Public Specification and Understanding FXG. Quickly browsing through them, I got a strange feeling of "Not invented here syndrome" or "we definitely like a balkanized world" deja-vu.
None-the-less, it is just a warning for now so I moved on to load the same svg file as an external resource. It requires to use an url loader in some way with code like:
public function fetchGraphics( id:String, img:Image ):void { var ld:Loader = new Loader(); ld.contentLoaderInfo.addEventListener(Event.COMPLETE, function (ev:Event) : void { var loader:Loader = Loader(ev.target.loader); trace("[ResourceManager] loading completed"); img.source = loader.content; }); var request:URLRequest = new URLRequest("baby_chick.svg"); ld.load(request);
Sounds easy right? Well many things are going here that I had to find out the hard way. First even though the ld.load returns before the content available, the COMPLETE callback is not called from a spawn thread. If you try to wait until img.source != null in a loop after the ld.load, it will just hang the swf.
Once I passed through to understand that event.COMPLETE behavior, I stumbled upon the "svg is supported as embed graphics but not as an external resource" issue. Finding good free .swf sprite samples is not as easy as finding .svg files. I finally got one out of the SDK examples. Eh Google is not always helpful; sometimes you have to rely on your local system find command.
I then hit a security error loading my file from my localhost apache instance. As I am debugging this issue, I realized the requests did not even hit my server; i.e no trace of the request in the /var/log/apache/access_log. It turns out that the flash player caches downloaded files locally. Forcing a request to bypass the cache goes from appending a unique identifier to the request (something like "?nocache=" + new Date().getTime()) to setting various headers in the request like the following.
var header:URLRequestHeader = new URLRequestHeader("pragma", "no-cache"); request.requestHeaders.push(header); header = new URLRequestHeader("refresh", "0"); request.requestHeaders.push(header);
For now it does not really matter, I still get a security error to deal with. I looked through Curtis Morley's blog and others but it turned out the exception was triggered because the permissions on the requested file were wrong (no group/world read permissions). That one is definitely nominated to the most cryptic error messages.
Creating a button with an image
The next road block I scratched my head on was to create a button out of an image. You think it should be straightforward but it is not. The easiest shortcut seemed to set a few properties in actionscript 2.0 mx.controls.Buttons. Unfortunately those buttons could not be added into the FlexGlobals.topLevelApplication so I had to learn how to create custom skinned spark buttons.
Google shows up a lot of e-mail threads with questions on the same basic problem. I though could not find anything satisfactory that I could use. I finally resorted to the genius idea of running the following command on my local system.
$ find ~ -name '*Button*.as'
That located a few source files in the Flex SDK that I could read and model my code on.
Drag and drop
Drag-n-drop functionality is built into the Sprite class of the interface framework. That should be a breeze but here again a little problem showed up.
There does not seem an easy way to clone an Image instance or any DisplayObject instance for that matter.
Many people recommend to draw the component in a BitmapData instance since those could be duplicated. That defies the whole purpose of using vector graphics so this was not an alternative for me.
I tried different implementations of a cloneObject function but always ended-up with an exception thrown at some point either on a cast or when adding the new object to the hierarchy.
Finally, among all things the following code seemed to do what I wanted even through it looks like it should not.
protected var imageDisplay:Image; ... protected function onMouseDown( evt:MouseEvent ):void { var created:Image = imageDisplay; addElement(created); var p:Point = created.globalToLocal(new Point(evt.stageX,evt.stageY)); created.x = p.x; created.y = p.y; created.startDrag(true); }
Finally the last issue remaining with the drag-n-drop was to figure out who received the mouseUp event in order to issue a created.stopDrag. As it looks by putting different trace() statements, the first event handler called is the parent of created, then going up the tree to the root.
Conclusion
There are two things that are very disconcerting when starting ActionScript programming. First, the error messages are, to say the least, very cryptic. Most times, you will have to google the error code hoping to find someone that already figured out why you might get the error and what to do about it. Second, There are about three graphics frameworks to my account (flash, mx and spark). The class hierarchies are almost identical, using the same class names for the most part but they are all more or less incompatible between each other so be ready for a lot of casting issues until you realize the hierarchy that works for you and decide to stick with it.
I was amazed to stumble on so much little issues for things that you would think are just working in any decent UI framework (like image buttons). Many other actionscript developers struggle with the same issues. A lot of solutions and workarounds involve using bitmaps. That is just very puzzling for a product built around vector graphics. I will try out the same experiment in HTML5 and Javascript soon. Stay tuned.