Hi! So for the last few months I've been working intensely on the ActionScript 3 port of Mine Blocks. I've converted tens of thousands of lines of code from AS2 to AS3, and in this process I've learned A TON about how AS3 works compared to AS2. I'm actually quite familiar with AS3, but porting from a MovieClip-based game was a totally different monster for me.
This post is mainly to describe the main differences, issues, and successes I've encountered when porting from ActionScript 2 to ActionScript 3.
Disclaimer: This assumes MovieClips and Spaghetti-coder ;)
THE BASICS
When porting, there are 3 stages of errors to address as you go through the game.
There are two passes by the compiler. One of the stages lists a massive amount of errors. The other stage lists only a couple errors at a time, and it can be irritating to continuously fix-build-fix-build-fix. Then there are the run-time errors. Some will break the game, others will throw a ridiculous amount of debug errors. There's a point where run time errors are just seen as simple bugs. That's when the main pain is over, and all is left is bug fixing as usual. xD
AS3 is clearly much, much faster.
The world generation of Mine Blocks in AS2 takes about 30 seconds, while in AS3 it only takes under 2 seconds! Incredible! I've read that AS3 was 10 times faster than AS2, but I thought that was an exaggeration! It's really not. Apart from the lag generated by the debugging output, I haven't really noticed any control latency or lag, even with such a complicated game as Mine Blocks.
This is a pretty good reason to port a game from AS2 to AS3.
No code can be written on an object.
Code can be on the object's timeline, or their parent's timeline, but not the object itself. Usually it's pretty easy to port this though - simply convert the onClipEvent to an event handler on the parent timeline. Key and mouse listeners are the same story. They're found in the KeyboardEvent and MouseEvent listener classes. :)
Going from attachMovie to addChild requires the name property to be passed to the new object.
So dropped items in Mine Blocks checked to see what their name was so they could recieve their item type, count, enchantments, etc. The only catch was that in attachMovie, you supplied the clip with an instance name, which set the "name" property. with addChild, you need to give the MovieClip a reference in a variable (most likely a tmp one), and then using that reference, set its name. Pretty simple. Causes headaches though.
Functions (and all other variables) can only be instantiated once.
In AS2, you'd "accidentally" write:
var a = 5;
var a = 5;
and no one would have a fit. But in AS3, the compiler gets a bit angry! Well the same goes for functions. In Mine Blocks, one MovieClip handled all of the different types of blocks, all on different frames. Some blocks had special properties, like flowing water, falling sand, grass checking for sunlight, healing crystals healing, and even leaves leafing. To fix the multiple instantiations, frame one has
var behaviorFunction:Function;
and any frame using a new function will use an anonymous function reference:
behaviorFunction = new function() { /* stuff */ };
It's such a simple fix, but it was a lot of work to change everything. Porting isn't easy!
TRICKY ISSUES
AS3 handles events differently.
For better or for worse, they're different. In ActionScript 3.0, they continue to run even when the instance of the MovieClips is removed from the display list. If you have a clip with an ENTER_FRAME event, then go to a different frame that doesn't contain that clip, the ENTER_FRAME event still runs. Additionally, if the clip is added to the display list again, the event will be running multiple times at once! This is due to how the garbage collector works in Flash. Do a search for articles about Flash's garbage collection to get a good idea how and why this happens - it's really helpful to know. Anyway, I had to come up with a few hackish ways to get past some of the issues created by this behavior.
The error you'll usually get from this:
"TypeError: Error #1009: Cannot access a property or method of a null object reference." wherever there's a reference to a different clip (including MovieClip(root) ). Basically, the MovieClip doesn't really exist, so its properties are all null, including its reference to root.
One way to fix this issue is to listen for when the MovieClip is removed. When it is, remove all listeners.
addEventListener(Event.REMOVED_FROM_STAGE, cleanupListeners);
function cleanupListeners(e:Event) {
removeEventListener(Event.ENTER_FRAME, ef);
removeEventListener(Event.REMOVED_FROM_STAGE, cleanupListeners);
}
A similar solution that fixes this issue is by starting the ENTER_FRAME event function with:
if(MovieClip(root) == null) return;
This hides the bigger issues though, so if you're using this post as help for your own project, only use this if you're absolutely certain this is the solution for your situation.
Changing frames instantly runs code.
This can cause a stack overflow (try putting gotoAndStop(2); on frame one, and gotoAndStop(1); on frame 2). A problem with this is that if you attach a MovieClip and then change its frame other than frame 1, the initialization code wont run, creating errors. Just be aware. This error will be a give away:
Error #1023: Stack overflow occurred.
Sending GET and POST is fairly different in AS3.
I'm not a professional at how to send and recieve from a server, so I shouldn't post the code to port, here. But there are some resources online to help with porting to it. If you guys really want, I'll write the code. :)
QUICK LIST OF DIFFERENCES
1) random(n) was removed. Here's a replacement for the random function for you (just make sure you reference it properly, through MovieClip(root) or something):
function random(n) {
return(Math.random()*n>>0);
}
2) new Sound(x) was changed. setVolume and setPan were moved to the soundTransform class, which can be applied to the soundChannel class. If I remember correctly, only 32 sound channels can play at once.
3) _x, _y, _width, _height, and _rotation all lost their underscores in AS3.
4) _root is MovieClip(root) in AS3.
5) _xscale and _yscale in AS3 are scaleX and scaleY. Also they are out of 1, not 100.
6) _xmouse and _ymouse have changed to mouseX and mouseY.
7) onEnterFrame = function{ ... } is an event in AS3. It would look something like this:
addEventListener(e:Event, someFunction);
function(e:Event) { ... }
8) hitTest(...) was replaced in AS3 with hitTestPoint() and hitTestObject(). I believe it's the same thing, just split into two functions.
9) int was added to AS3! The Number class is a floating point decimal, still. The int class is an integer.
10) When referencing to a property of an undefined element of an object/array in AS3, it will throw an error. For comparison, in AS2 it would be undefined.
11) swapDepths(...) in AS3 is now achieved through setChildIndex(...). Similarly, getNextHighestDepth() can be achieved through numChildren().
nn) Soooo much more.
CONCLUSION
There are a lot of interesting differences to be addressed when porting from AS2 and AS3. It can be a pain to convert, but there's always Find/Replace to help out. Some times it can be useful to comment out code when porting, so you can focus on certain errors first.
If your game or project is fairly big, I do recommend porting it to AS3. It's possible. If the project is as big as Mine Blocks, you need to take in account that it'll be about half of a year of work... but if that means you can benefit from the new features of AS3, then go for it! :D
If you have any questions related to porting AS2 to AS3, feel free to reply to this post.