2009-10-04

How to create an ActionScript 3 (AS3) flash movie (SWF) without a GUI

This blog post explains how to create a Flash movie completely automatically, without using a GUI, just by writing a program in the language ActionScript 3 (AS3). The post gives instructions for Linux, but they should work with minor modifications on other operating systems as well since the compilers to be used are either written in Java or they are available for multiple systems.

ActionScript is a language similar to JavaScript, with some differences and incompatibilities. The most important difference is that you can declare types of variables (strongly recommended in ActionScript 3), and it has class-based inheritance, so classes are declared and use differently from JavaScript, more similarly to Java. To get a quick introduction to ActionScript, read http://en.wikipedia.org/wiki/ActionScript. The reference manual is hosted by Adobe, at http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/. The filename extension is .as.

ActionScript has two major versions in used today: ActionScript 2 (AS2) is supported by Flash Player 7, 8, 9 and 10. ActionScript 3 (AS3) is supported by Flash Player 9 and 10. ActionScript 2 is about 3.5 times (or even more) slower than the JavaScript interpreter in Firefox 3.0 (SpiderMonkey). ActionScript 3, as implemented in Flash Player 9, has just about the same speed as Firefox 3.0 JavaScript. ActionScript 3, as implemented in Flash Player 10, can be up to 4 times faster than FireFox 3.0 JavaScript if int is used instead of Number for integer computations, and Vector.<int> is used instead of Vector. Both Flash Players 9 and 10 have a JIT engine that compiles ActionScript to native code, but this doesn't seem to become faster than Firefox 3.0 JavaScript in some number-crunching applications.

It is possible to call a JavaScript function from ActionScript and vice versa, using the ExternalInterface ActionScript class (flash.external.ExternalInterface). Please note that ExternalInterface doesn't work for the file:/// protocol due to security reasons (as documented by Adobe here) – you have to upload both your .swf and .html files to the same web server to make use of ExternalInterface.

To compile ActionScript 2 to an SWF file, the free MTASC compiler is recommended, which is very fast, works at the command line, and is said to be safer than the compilers written by Adobe. An example ActionScript2 script:
// Tuto2.as, an example ActionScript 2 script
import flash.external.ExternalInterface;
class Tuto2 {
static var app : Tuto2;
function Tuto2() {
_root.createTextField("tf",0,0,0,800,200);
_root.tf.mutliline = true;
_root.tf.text = "first";
var myformat = new TextFormat();
myformat.color = 0x00000;
myformat.underline = true;
myformat.size = 30;
// Set this, because the default font, "Times New Roman" may not be available on Linux.
myformat.font = "serif";
_root.tf.setTextFormat(myformat);
_root.tf.text = ExternalInterface.call + "\nHello";
// Call this again after changing the text, otherwise the formatting is lost.
_root.tf.setTextFormat(myformat);
}
static function main(mc) { // Entry point.
app = new Tuto2();
}
}
To compile it, download MTASM first:
$ wget http://www.mtasc.org/zip/mtasc-1.12-linux.tgz
$ mkdir mtasc
$ cd mtasc
$ tar xzvf ../mtasc-1.12-linux.tgz
$ cp .../Tuto2.as .
Then compile with:
$ ./mtasc -version 8 -swf tuto2.swf -main -header 800:200:20 Tuto2.as
The corresponding HTML would look like this for Firefox:
<embed src="tuto2.swf"
quality="high" bgcolor="#eeeeee" fgcolor="#000000" width="800" height="2
name="tuto" id="tuto" align="middle" allowScriptAccess="always"
allowFullScreen="true" type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer" />
To make it cross-browser, use SWFObject to embed it. Once created, load the HTML in your browser to view the Flash animation you've just created.

From now on we focus on ActionScript 3 and Flash Player 10. Adobe provides the Flex SDK, which contains a compiler (called mxmlc, or mxmlc.exe on Windows) which can compie ActionScript 3 .as source files to .swf. The Flex SDK is free to use and is partially open source, and it is implemented in Java. Download version 3.4 (which is the most recent stable version at the time of writing this blog post, and is about 120 MB compressed) from http://opensource.adobe.com/wiki/display/flexsdk/Downloads. Also make sure you have Java (at least a JRE) installed. Here are the corresponding download command-lines:
$ wget http://fpdownload.adobe.com/pub/flex/sdk/builds/flex3/flex_sdk_3.4.0.9271.zip
$ mkdir flex
$ cd flex
$ unzip ../flex_sdk_3.4.0.9271.zip
$ bin/mxmlc -help
Here is a sample ActionScript 3 program:
// Tuto3.as, a sample ActionScript3 program
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.utils.getTimer;
import flash.external.ExternalInterface;
public class Tuto3 extends Sprite {
function Tuto3() : void {
var circle : Sprite = new Sprite();
circle.graphics.beginFill(0xFF7744);
circle.graphics.drawCircle(0, 0, 30);
circle.graphics.endFill();
addChild(circle);
//myInit();
//addEventListener(Event.ADDED_TO_STAGE, myInit);
ExternalInterface.addCallback("myFunction", myInit);
}
public function myInit(e : Event = null) : String {
var startTime : Number = getTimer();
var tField : TextField = new TextField();
var tFormat : TextFormat = new TextFormat();
tFormat.size = 20;
// Set this, because the default font, "Times New Roman" may not be available on Linux.
tFormat.font = "serif";
tField.autoSize = "left";
tField.background = true;
tField.border = true;
tField.multiline = true;
tField.x = 20;
tField.y = 40;
tField.text = "hello1";
tField.setTextFormat(tFormat);
var endTime : Number = getTimer();
trace('hello2 ' + startTime + ' ' + (endTime - startTime));
tField.text = 'ms=' + (endTime - startTime);
// Call this again after changing the text, otherwise the formatting is lost.
tField.setTextFormat(tFormat);
addChild(tField);
}
}
}
Here is how to compile it (creates Tuto3.swf):
$ ./bin/mxmlc -target-player 10 -default-size 800 200 -optimize Tuto3.as
Here is the corresponding HTML:
<html><head><script type="text/javascript">
function myonload() {
// document.tuto for Firefox, window.tuto for Internet Explorer
var tuto = document.tuto || window.tuto || document.getElementById("tuto");
if (tuto && tuto.myFunction)
document.getElementById("result").innerHTML = tuto.myFunction();
}
</script></head><body onload="myonload()">
<embed src="Tuto3.swf"
quality="high" bgcolor="#eeeeee" fgcolor="#000000" width="800" height="200"
name="tuto" id="tuto" align="middle" allowScriptAccess="always"
allowFullScreen="true" type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer" />
<div id="result">no result yet</div>
</body></html>
The HTML above also demonstrates calling an ActionScript method form JavaScript. To call a JavaScript function from ActionScript, do
ExternalInterface.call("JavaScriptFunctionName", arg1, ...)
Don't forget to copy the .html and the .swf to a http:// location, otherwise ExternalInterface won't work because the security limitations imposed by the Flash Player.

Please note that according to Adobe, AVM2, the virtual machine in Flash Player 9 and 10 for ActionScript 3 execution does a JIT for all functions and methods except for constructors. So don't put performance-critical code to the constructor. I couldn't measure any speed difference in the constructor and other methods. The last statement that the constructor is not JITted I could find is from 2007 (http://blog.pixelbreaker.com/flash/as30-jit-vs-interpreted/).

1 comment:

nanu said...

I did not find any speed difference either. I suspect the accuracy of the statement that constructors are interpreted.

As per the documentation $init and $cinit are interpreted - $init and $cinit are not the same as your own class's constructor - I think the class constructor that you write is jitted unless you use "statics" or something else that breaks jitting - Using statics moves the "static" defn into $init and and is interpreted but the rest of the constructor seems to be left intact to be jitted... The reason I think contructors are jitted is 'coz they are not part of $init or $cinit which are the only interpreted sections as per documentation. My experimentation also seems to indicate no performance improvement either whether you put the code in the constructor or elsewhere...

If there is a piece of code that behaves differently, i'd like to see it and convince myself that my theory is absurd!