Migrating to Flex mxmlc from ActionScript 2 mtasc

542 views Asked by At

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.

2

There are 2 answers

3
J. Holmes On BEST ANSWER

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);

0
Garret Wilson On

Thanks to 32bitkid for the comprehensive response. I finally got this working. Just to fill in answers for the remaining questions, here are some of my experiences in this long day of experimentation.

First of all, to get a main class, you simply indicate that class on the mxmlc command line. The class doesn't have to be in the root package. Put the startup stuff in the class constructor. Oh, and from other web sites, it appears the class may need to extend Sprite or movie clip.

None of the bind stuff is needed, as 32bitkit indicated. But watch out---much of the API has changed since Flash 9. For instance, ExternalInterface.call() now only takes two arguments, because you don't need to pass the context (e.g. this) anymore. And the Sound API has completely changed.

Trying to define Array.from() probably won't work. (I didn't need it after throwing out my Function.prototype.bind() that I didn't need anymore.) Trying to augment existing classes such as adding a String.prototype.endsWith() doesn't seem to work either.

But perhaps the most important thing I found regarding debugging SWF that finally allowed me to figure out problems is very important: go download the debug ActiveX Flash player and install it for IE. (I'm only recommending the IE version because the whole point of this exercise is that I'm trying to get WAV files to play on IE---the only major browser that doesn't support WAV files. Grumble grumble.) Seriously, the debug Flash player is invaluable. The standalone debug player doesn't support ExternalInterface.

Oh, and an easy way to help with debugging? Just call back to the browser's logging routines, like this:

ExternalInterface.call("console.info", "Hello, world!");

It works like a charm! Oh, and one last thing: turn this on immediately at the start of your code, and when you call your SWF from JavaScript, the exceptions will come back and show up in your browser's debugger (and vice-versa)!

ExternalInterface.marshallExceptions = true;

I've spent the whole day being forced to move up to ActionScript3 and Flash 10 and Flex, etc. when I thought I'd only modify a couple lines of code. Now that I at least have things connected, thanks in large part to the feedback here, tomorrow I'll jump back in and try to find out why I can't get WAV files to play---which was the whole point of this exercise to begin with.