Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Making the leap from AS3 to AS2

Posted September 15, 2009 by senocular

I recently ported (most of) a small to medium sized Flash-based application from ActionScript 3.0 to ActionScript 2.0 - yes, that's going down not up - and I thought I'd share some insight gained from that experience.
First, while at one point in time I considered myself proficient in AS2, I've realized that I've forgotten a lot, and that didn't help me very much in my quest. But over time, things started to come back and it got easier. There's a lot I remember loving about AS2 but also a lot which, honestly, pisses me off.

The application being ported was built in Flash CS3/CS4, targeting both Flash Player 9 and 10. There was nothing specific to 10 about it (vectors, etc.); it's all pretty much your standard, baseline AS3. There was very little timeline integration other than the hierarchical layout and positioning of components, both custom, and those native to the Flash authoring too. All logic exists in external classes, including a class defined for the document class. In total, there was a little more than 14,000 lines of code.

First steps. My first step was to make the easy conversions through good old fashioned search and replace. I thought it was a good idea at the time. The easiest and most typical conversions included:
  • void = Void
  • function() = function(Void)
  • int,uint = Number
  • Sprite, DisplayObject, DisplayObjectContainer = MovieClip

As you may have guessed, and I knew this a little going on, that these wouldn't work out perfectly in the end. But it was a little worse than I thought. For one, (1) function(Void) apparently does not work for getters in AS2 - and I had a lot of those. That made that conversion a bust. Then (2) there was the more devastating int conversion. This was bad for two reasons. For one, I had a custom print() method I was using for debugging. That ended up becoming prNumber(). Secondly, there were a lot of places in the code that was using int() as a Math.floor(). Replacing those instances with Number(), obviously, didn't work. Last but not least (3), as it turns out, MovieClip does not work for things like Bitmap or TextField when DisplayObject does. So there was some breakage in that conversion as well.

Fair enough. Problems there was expected. And other conversions for changes in syntax also needed to be taken care of, such as:
  • removing the package {} (and placing package path with class name)
  • protected = private
  • internal = public
  • default parameters had to be removed (handled in function body)
  • casting with 'as' to be replaced with Type() casting
  • type checking with 'is' replaced with instanceof, typeof (for primitives), and Type() casting (for interfaces)
  • stage = Stage
  • all MovieClip properties which in AS2 start with "_"

These were mostly handled individually as each class was being manually converted, though search and replace also played its part.

Its worth pointing out that private in AS2 is not as private as it is in AS3. There is no absolute private scope in AS2 which caused a problem with collisions from inheritance in one or two of the AS3 classes which had similarly named private members. A rename took care of this, as well as similar issues with the AS2 MovieClip API - I had a private _target class member which conflicted with MovieClip._target which took me a while to figure out.

Next was the changes of behavior. These included things like:
  • complex class member values had to be instantiated in the constructor (rather than class body with declarations)
  • alpha from 0-1 in AS3 to 0-100 in AS2
  • getBounds doesn't return a Rectangle in AS2

getBounds was a little frustrating, where in AS3 you get a Rectangle instance and in AS2 you get a generic object with xMin, xMax, yMin, and yMax properties. That caused a problem in a few places, but was easily fixed by using a conversion function to translate the AS2 getBounds result to a Rectangle data type since, luckily, Rectangle has been around since Flash Player 8. This was after the getBounds method was introduced which is why it's not used there. But wait, there's more! getBounds doesn't exist for instances like Button or TextField! As an alternative, Transform.pixelBounds was used instead.

The above mentions the "simple" differences of behavior. The big one is with event handling. Oh, how that has changed. Not only is event handling different in AS2 (and diverse in it's approaches), but delegates are necessary to support the built-in method closures of AS3. What this means is that 'this' in a function in AS2 is dependent on where that function is assigned. A class method does not retain a reference to its associated class instance, not unless forced through unconventional means like (Flash's) mx.utils.Delegate, which I obviously had to make heavy use of.

Moreover, in what I suspect was a choice of convenience, I ported the EventDispatcher class of AS3 to AS2. This made event handling more or less consistent between the two versions of the application. The only difference was for low-level event handling (like onPress/onRelease) and the dependency on create delegate versions of each handler to be used within add/removeEventListener calls. Other classes like Timer, were also ported. Though there are similar AS2 APIs, having consistency between the two ports of the application was favorable.

The exposure to some of the lower level events made it also apparent that event handling was very limited in AS2. For example, there are no events related to failures for the LoadVars class. You simply have one callback, onData/onLoad which has a result or doesn't. Similarly, the plethora of try..catch statements littered throughout the code were unnecessary in AS2 - not unless I was throwing my own errors, which was unlikely at best. This meant a different approach in error handling relying more on the existence of values rather than the handling of thrown exceptions. What were try..catches became if..elsess.

And if that wasn't bad enough, lets talk about the timeline. Specifically, the dynamic creation of MovieClip instances in AS2. For those, you need to use attachMovie; you cannot instantiate instances using new MovieClip() (or respective MovieClip-based class). This required more syntax conversions, but more importantly, required drastic revisions in the handling of persistent objects across "states" that in AS3 would have been removed from the display list but, in doing so for AS2, would require destroying the object. In some cases, toggling visibility was enough, but not all. Data for movie clip instances had to be retained in a separate object which could be kept independently of the existence of the movie clip on screen.

There was one last major hurdle in this port. It can be summed up in 3 simple words: Ex, Em, Elle. While I have my complaints about E4X, there's no doubt that it's a heck of a lot easier to work with than the old XML model in Flash. Luckily, this application wasn't using XML in such fantastic ways that simple helper functions couldn't be created to handle most of the E4X operations, but I think that's only because I was lucky. I have come across other situations where the conversion was much more heinous, but that's not something that was a problem here. The old XMLNode object did a good job replacing XML in mot places while Array handled XMLList in others. with a little looping and an attributes reference to replace @ for attributes, and I was pretty good.

In the end I'll have to say the bigger problems, like events and to a lesser extent the display list, were more fun to deal with and their solutions a little more satisfying. It was the little things (syntax, API changes) that really annoyed me and made it hard to create and maintain an AS2 port. I do know that I certainly could have planned better had I known about some of these difficulties when writing the application in AS3 originally.