Search code examples
flashactionscript-3apache-flexmxmlcmtasc

Migrating to Flex mxmlc from ActionScript 2 mtasc


Years ago I created a tiny, simple SWF just to play MP3 files on various browsers. I exposed interfaces and called them from JavaScript. It was all small and straightforward; you can see the entire Guise.as source code.

I compiled the main class using mtasc, which allows you to specify a main function for initialization code:

/**Main entry point.*/
static function main(mc)
{

This was working just fine until I wanted to add WAV support. Flash doesn't natively support WAV, so I tried to add in a library. But the library required Flash 10 and wouldn't compile on mtasc, so I downloaded Flex 4.6 and tried to use mxmlc. Boy, was my pain only beginning.

My compiled SWF no longer works---even for MP3 files. I don't know where to start finding the problem, but I know I have lots of unanswered questions---maybe one of them is my problem:

  • If I understand correctly, mxmlc doesn't have the concept of a "main entry point", but Flash will simply create an instance of the "main class"', whatever that is. But how do I specify the main class? If I reference my class with mxmlc on the command line, will that class automatically become the main class, or is it absolutely required that my class be in the root (i.e. in no) package? Does it have to have a special name?
  • After I successfully designate a main class, can I simply move my entry point code into the constructor of my main class?
  • In my original class, I added a global function to convert an object to an array using Array.from=function(object:Object). This gave me an error while I was in strict mode---apparently because it doesn't like me to add static methods to the Array class object. Will this still work in non-strict mode? What's the problem? If I convert it to a normal method on my class, will it work?
  • As I'm accustomed to doing in "real" JavaScript, I added a Function.prototype.bind=function() function so that, when I have callbacks, this will be set correctly. Will this still work? Can I add methods to the prototype of Function?
  • Do I even need to bind the context anymore? If I call something like positionTimeoutID=setTimeout(fireSoundPosition.bind(this), 1000), without bind(this), will Flash pass the correct this to my callback method?
  • The Flex compiler complained that several API methods had changed, so maybe modifying my calls changed something and I don't understand the new API. Is there any way to debug this SWF? Write to the browser console? A beep? Anything? Without buying some big IDE from Adobe or something?

Any feedback would be appreciated. I'm sure there's just one or two little adjustments that's throwing the whole thing off, but maybe with a little help from the community I won't have to spend several days reading entire books and buying new SDKs just to recompile my SWF with a couple of new calls... Thanks.


Solution

  • I don't think I can answer all of your question, but I'll try to provide some answers:

    ActionScript 3 is a substantial change from ActionScript 2. Its a complete architectural overhaul, not just a minor update and its not backwards compatible, so short of a re-write, its usually pretty difficult to tweak non-trivial as2 to compile as as3. It's pretty much like an entirely new language . So it might be best to take a step back and see whats changed in the language, because it's a lot.

    The biggest thing is the formalized class inheritance, over prototypical inheritance.

    • "Flash will simply create an instance of the "main class"', whatever that is."

    So, when you compile from the command-line, you give it the path to the "main class":

    mxmlc.exe "c:\dev\project\SomeClass.as"

    with SomeClass.as looking like this:

    package {
        import flash.display.Sprite;
        public class SomeClass extends Sprite {}
    }
    

    Upon initialization, flash will create an instance of this class and attach it to the stage. This will be the similar to the AS2 concept of _root. The -src switch passed to mxmlc.exe sets the path to the rest of the classes/packages that support this main class.

    As such, your main class, whatever you call it, should inherit from Sprite.

    • After I successfully designate a main class, can I simply move my entry point code into the constructor of my main class?

    Yes. The constructor for your "main class" will be the entry point for your swf.

    • "As I'm accustomed to doing in "real" JavaScript, I added a Function.prototype.bind=function() function so that, when I have callbacks, this will be set correctly. Will this still work? Can I add methods to the prototype of Function?"

    ActionScript 3 class methods are automatically bound methods, which is a subtle change from javascript. In fact, its impossible to call a class method in any other context than that instance from where it was created (even if you use .call() or .apply() to try to force a context change). For example with this simple class

    public class SomeClass {
        public function Worker() {
            alert(this);
        }
    }
    

    and then

    var cls:SomeClass = new SomeClass();
    
    cls.Worker();
    var func:Function = cls.Worker;
    func();
    func.call(this);
    func.apply(undefined);
    

    Those four function invocations will produce the exact same result, because Worker() is always bound to the function it came from.

    Note, this only applies to class methods, and doesn't apply to anonymous functions/closures. So...

    var func:Function = function():void { alert(this); }
    func();
    func.call(cls);
    func.call(undefined);
    

    ...are all different

    • "Do I even need to bind the context anymore? If I call something like positionTimeoutID=setTimeout(fireSoundPosition.bind(this), 1000), without bind(this), will Flash pass the correct this to my callback method?"

    It depends, if its a class method then it'll always be bound (see last section). If closure/anonymous function, then yes, it'll still need to be bound to specify this.

    • The Flex compiler complained that several API methods had changed, so maybe modifying my calls changed something and I don't understand the new API. Is there any way to debug this SWF? Write to the browser console? A beep? Anything? Without buying some big IDE from Adobe or something?

    You'll probably want to go get the flash debugging player. And the compiler should have come with fdb, the flash command-line debugger. The idea is that when you host/run your app in the debugging player, you can attach fdb to the instance and trace(), as well as set breakpoints and view exceptions.

    • In my original class, I added a global function to convert an object to an array using Array.from=function(object:Object). This gave me an error while I was in strict mode---apparently because it doesn't like me to add static methods to the Array class object. Will this still work in non-strict mode? What's the problem? If I convert it to a normal method on my class, will it work?

    I'm going to have to look into this one, though I would imagine that the "proper" AS3 solution, would be to create a static method off of another class to perform this action, rather than trying to extend Array directly. Something like:

    package {
    
        public class ArrayHelpers {
            public static From(object:Object):Array {
                /* do work */
            }
        }
    
    }
    

    And then call it as: ArrayHelpers.From(whatever);