Adobe, MAKE SOME NOISE

Quick post today. If you're making the jump from timeline coding to class based development (and I definitely encourage you to!) you'll undoubtedly run into this problem. It goes something like this:

  1. Designate a Document Class for your fla - let's call it ProjectMain
  2. Create the ProjectMain class and save it alongside your fla as ProjectMain.as
  3. Set a property of something on stage, let's say myCoolLogo.x = stage.stageWidth / 2;
  4. Compile to receive your TypeError: Error #1009

    Your code is otherwise fine - commenting out that line seems to fix everything. Furthermore the object is right there on stage in your fla – your constructor just refuses to acknowledge it!

    The problem goes back to AS2 prehistory and the top-down/bottom-up problem. If you don't remember that (admittedly it's a little obscure even if you did work in AS2 a lot), here's the gist: Flash is single-threaded, meaning it can only do one discrete task at a time, which includes running your constructor, and parsing everything on stage. In AS2 you could specify whether you wanted to run your code "top down" - meaning, 'run all the code in the top layer, then the next below and so on' or vice versa for "bottom up."

    At this point you should be wondering what this has to do with anything - well let's say you create a variable on layer 10 (top layer) and give it a value - and in layer 1 (bottom layer) you wan't to do something with that value - it's only going to work if you're running top-down. If you run that fla bottom-up you'll execute the layer 1 code first and the variable will be undefined until frame 2. The exact same thing is happening with your constructor in AS3.

    The constructor will always precede any timeline code or bytecode (including positioning things on stage, tweens, &c.) - so you need to be careful with your initialization. It's alright to create new objects and work with them, but you can't query anything that you put down on stage (the same goes for library-linked Classes) until at least one render cycle has passed and allowed all those objects to be generated. Luckily, AS3 has an event for that so it takes some of the pain out.

    Here's how:

    1. // ...
    2.  
    3. //--------------------------------------
    4. // CONSTRUCTOR
    5. //--------------------------------------
    6.  
    7. public function ProjectMain(){
    8. super();
    9. addEventListener(Event.ADDED_TO_STAGE, function(e:Event):void{
    10. e.target.removeEventListener(e.type, arguments.callee);
    11. myCoolLogo.x = stage.stageWidth >> 1;
    12. });
    13. }

    If you're curious about line 39 in there, it's a generic way to remove event listeners - there's a little more detail over on the code page. Also as an added bonus, all the code has been moved into a second function - and since the ActionScript JIT compiler doesn't optimize constructors, there's a good chance that this code will run faster than before.

    There are a handful of questions that get posted over and over again in the forums that all boil down to the same issue: setting up instance names in AS3 doesn't work.

    Everyone who hits this problem comes up with a plan of attack that goes something like this:

    1. for(i = 0; i < 5; i++){
    2. var mc:MovieClip = new MovieClip();
    3. mc.name = "mc" + i;
    4. addChild(mc);
    5. }
    6.  
    7. mc3.play();

    Which sounds pretty good on paper, but generally ends with this:

    1. Error 1120: Access of undefined property mc3.

    So what went wrong? This is easily one of the biggest stumbling blocks for new AS3 users, and Adobe goes to great lengths to ignore it and pretend the problem doesn't exist. Here's the secret: Instance names don't really exist!

    Ok, more specifically, when you assign an instance name in Flash, there is secret code generated for you behind the scenes that creates two variables which work together to give you the perception of an instance name. To do the same thing in your own code you would use this:

    1. public var mc3:MovieClip = new MovieClip();
    2. mc3.name = "mc3";

    A .name is not a thing

    The first half of this solution is repairing a misconception; There is a general belief that setting the name property on an object gives it mystical powers. It doesn't. The name is just a string that is attached to objects for your convenience, it is not a unique identifier. Saying you want to play the clip named "mc3" is just as silly as saying you want to play the clip at .x = 50, or .alpha = 1 – these are all attributes of objects, not pointers to the objects themselves.

    So with all of that said, now it's time to contradict myself. Adobe has tried to redeem themselves by making a little workaround function for this problem. It comes in the form of the getChildByName("mc3") method. It's not particularly efficient and you can have multiple things share one name so it's not great code practice, but it is a handy tool to be aware of in a pinch.

    Create dynamic variables at runtime

    Having completely debunked .name, we've now got the other half of this issue to deal with - how to create dynamic vars in AS3. Again the answer is, you can't. What you can do, is leverage a single Array to point at all your clips at once.

    1. var clips:Array = [];
    2.  
    3. for(i = 0; i < 5; i++){
    4. var mc:MovieClip = new MovieClip();
    5. mc.name = "mc" + i;
    6. addChild(mc);
    7. clips[i] = mc;
    8. }

    That code will create 5 distinct MovieClips and stick them all into the clips Array under different indices. To get a specific clip back later, you just have to know the index:

    1. clips[3].play();

    I haven't posted in ages so I'm throwing out a quickie to get myself back in the rhythm. I don't have anything super-cool today so I'm going to kick off a series of posts for users who are jumping from AS2 to AS3. Mostly these will just be answers to super-common questions out on the forums so I can stop re-typing them all the time.

    I see some version of this question posted almost daily: "I'm new to AS3 and I have a parent class and a child class; how can I call a function in the parent class from inside the child?"

    By this time you've already tried parent.someFunction() and gotten this

    Error 1061: Call to a possibly undefined method someFunction through a reference with static type flash.display:DisplayObjectContainer.

    The problem is that the child class is essentially deaf and blind - it has no way of knowing what kind of object the parent is (it could be a Sprite, MC, stage, custom class, whatever) so Flash has to be safe and restrict you to the common denominator, DisplayObjectContainer, from which all those other classes derive. Because of that, the .parent property will only allow you to call DisplayObjectContainer methods on it such as removeChild, getChildByName, mouseEnabled, &c.

    So right here there is a fork in the road – you can make a couple of assumptions and get a quick and easy answer and be done, or you can do a little reworking of your code and get to the bottom of the problem once and for all.

    The Easy Way

    Since most custom classes extend MovieClip, we can tell the compiler to go ahead and open up that functionality to us by treating .parent as a MovieClip instead of just a DisplayObjectContainer:

    1. MovieClip(parent).someFunction();

    That line tells Flash that the parent is a MovieClip (or some offshoot) and therefore is a dymamic class – meaning you can add properties and public functions to it on the fly – so you can call anything you want on it and the compiler will search for it instead of falling back to what is explicitly stated in the class at compile-time.

    If that's a no-go for you (say your parent is a Sprite, for example), you can still use this trick but you'll need to target the functionality of the main class a little more specifically.

    1. import flash.display.Sprite;
    2. import MainClass;
    3.  
    4. public class ChildClass extends Sprite{
    5.  
    6. // ...
    7. MainClass(parent).someFunction();

    The Hard Way

    Ok - judge me if you want but I've already spent way too much time on the first half of this post and I'm not even to the good stuff yet. I'm going to hammer out the second half of this and post it a little bit later so that this whole concept is more digestible. Continuation is imminent!

    continued: AS2 to AS3: Calling a Method of a .parent Class (2 of 2) ⇢