Object-Oriented MovieClip Callbacks

It’s Day 2 of Jason’s Actionscript 2.0 Bootcamp! The first step was to port his AS1.0 code into AS2.0 classes. We’ve gone as far as just creating all our components on the screen, but have not yet hooked up any events. Why? Because they’re tricky in AS2.0 (at least when you are rolling your own). For example, we had to go over how MovieClip callbacks in an object-oriented context doesn’t work. Witness this class definition:

class Foo {

var mc :MovieClip; var myProperty :String;

function Foo ( parentClip:MovieClip ) { mc = parentClip.attachMovie (“FooImage”,”Foo1″,1); mc.onPress = HandleOnPress; }

function HandleOnPress() { trace(“myProperty == ” + myProperty); }

}

When you run this, the output will say “myProperty == undefined”. The reasoning is a bit esoteric: The MovieClip class uses old callback conventions that don’t maintain object context in ActionScript 2.0…you’re just setting a pointer to the address of a stream of code, not a method being invoked on an object. With the object context, you can’t access any variables in the object you’re defining your code in, though it certainly almost looks like it should be fine.

Why they didn’t add MovieClip listeners in MX 2004 baffles me. Maybe they didn’t want to add duplicate listeners in an already confusing API? But then why did they go ahead and do that in the TextField class? You can also use the Mouse and Keyboard object listeners instead to do the same thing I’m describing. But I digress.

Anyway, I showed him the workaround. Here’s the modified constructor:

function Foo ( parentClip:MovieClip ) { mc = parentClip.attachMovie(“FooImage”,”Foo1″,1); mc.parentObject = this; mc.onPress = function() { this.parentObject.HandleOnPress() } }

The key to this is the this keyword. It means two different things here. In the first line, it refers to the instance of Foo that’s being created. In the anonymous function, this evaluates to the MovieClip that is triggering the callback function. If you didn’t know that, you’d be screwed! So, we’re using this behavior to re-establish object context in the callback without a lot of tedious manual lookup using associative arrays (which I’ve done in the past). MovieClip is a skanky object…it’s not too discerning about what you stick into it during runtime, if you catch my drift. The parlance in AS 2.0 is that it’s a “dynamic object”, requiring no declaration of properties before you can use them. You know, just like good ole horrible Actionscript 1.0, with its “call me and I’ll be there but useless and non-error generating so you’ll never know you misspelled ‘proerty’ in 1000 lines of code but hey it makes the language seem more friendly” insanity. Gah!

After Jason finished porting his code, we’ll talk about the need for explicit destructors, as using a composition class like this for MovieClip sort of defeats some of the magical garbage collection features of Actionscript 2.0.

UPDATE MAY 12, 2006:

Since writing this article, I found a neat trick from Colin Moock’s Essential Actionscript 2.0 book. It’s just mentioned in passing, but it’s very critical. Check out pages 300 and 308 about this use of an intermediate variable thisInstance to hold the value of this.

class Foo {

var mc :MovieClip; var myProperty :String;

function Foo ( parentClip:MovieClip ) { mc = parentClip.attachMovie (“FooImage”,”Foo1″,1); // dereference ‘this’ var thisInstance:Foo = this; mc.onPress = function() { thisInstance.HandleOnPress() } }

function HandleOnPress() { trace(“myProperty == ” + myProperty); }

}

The use of the local variable thisInstance saves a copy of this so it refers specifically to the instance being created. So, when the anonymous function assigned to mc’s onPress callback executes, it actually refers to the instance instead of the this keyword. Remember, the this keyword, in the context of an anonymous function used as a callback handler, refers to the MovieClip. This is a very handy trick.

If you’re using event listeners with components, you’ll be looking instead into using Macromedia’s mx.utils.Delegate class to create the handler for the addEventListener() call. Works great in conjunction with Grant Skinner’s GDispatcher class.