1
votes

I've searched around and found similar questions to this, but the answers (accepted ones, even!) are either hacky, appear to have rather little to do with the actual question, or just aren't working when I try them.

I'm trying to reference objects on the stage in the current frame from a document class in Actionscript 3 in Flash CS3. In this example, I'm trying to get at a dynamic text field with the instance name "question_txt", but there are also buttons and other things I'll need to get at to put event listeners on and such.

I have "Automatically Declare Stage Instances" checked in the publish settings, so the references should be there -- in fact, if I try to declare them in the class, I get errors about a conflict with the name -- but when I try to reference these objects (in any of several ways I've now tried!) I ALWAYS GET NULL.

package {
    import flash.display.MovieClip;
    import flash.display.SimpleButton;
    import flash.display.Stage;
    import flash.text.TextField;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.net.URLLoader; 
    import flash.events.Event;

    public class DecisionTree extends MovieClip {

        private var tree_xml:XML;
        private var current_node:String;

        public function DecisionTree():void {
            trace("Constructor");
            tree_xml = new XML();
            tree_xml.ignoreWhitepace = true;
            loader = new URLLoader();
            loader.addEventListener(Event.COMPLETE,
                function(e:Event):void {
                    tree_xml = new XML(e.target.data);
                    goToNode('main');
                });
            loader.load(new URLRequest("decision_tree.xml"));
        }

        private function goToNode(name:String):void {
            trace("goToNode " + name);
            current_node = name;
            node_xml = tree_xml.node.(attribute('name') == name)[0];
            node_frame = parseInt(node_xml.attribute('frame'));
            node_question = node_xml.question.text();
            gotoAndPlay(node_frame); // will goto frame 35 where the stuff is

            // ARRRRRGH EVERYTHING'S COMING UP NULL!!!
            // TypeError: Error #1009: Cannot access a property or method of a null object reference.
            question_txt.text = node_question;

            //q = question_txt as TextField;
            //q.text = node_question;

            //qtxt = stage.getChildByName('question_txt') as TextField;
            //qtxt.text = node_question;

        }
    }
}
4

4 Answers

4
votes

The simple fact is that you cannot rely on this to work:

gotoAndStop(2);
thing_on_frame_2.foo(); // no guarantee this will work.

Chances are that the other answers you've seen look hacky because it is but there's no better way around it. The playhead will not actually be guaranteed to be on frame 2 immediately after you call gotoAndFoo(), and if it is there's no guarantee that the objects placed there in the fla will have been instantiated by the time you attempt to access them. This used to be legal and valid in AS2, but in AS3 it is no longer.

Typically, you would do one of the following to solve this:

  1. Make sure the referenced object exists on all frames of the timeline.
  2. Add a framescript to act as a callback that lets you know when you've reached the frame in question. This can either be a framescript written in the fla, or one that you add at the class level using the addFrameScript function.

Optionally, if you're only concerned about listening for button clicks then instead of adding the listeners to the buttons directly you could add the listener to the topmost container of the buttons and then check the instance name of the object that dispatched the event before deciding what to do about the event. I favor this method as it's easier to keep track of what is and is not a listener.

Hacky or not, that's just how it's done.

EDIT with more info:

I know that if you trace the current frame the number returned will be the one you just instructed the playhead to move to. The problem is that there is no guarantee that objects on the stage in the fla will or will not be instantiated by the time you request them in the code. That's why you use a framescript, as the objects are guaranteed to have been instantiated by that time.

As to the question of whether there are events you can listen for that are guaranteed to dispatch after construction, the answer is "maybe. What version of Flash Player are you targeting?" The most commonly known frame event is ENTER_FRAME because it's the one that's been supported from the beginning. Over time we've been given EXIT_FRAME, FRAME_CONSTRUCTED, and the like that all dispatch at different times. FRAME_CONSTRUCTED is the one that you'd want to listen for, but it's not very wieldy or practical to add a FRAME_CONSTRUCTED listener for just the one split second you need it and then remove it immediately, and of course will not compile if you are forced to work in CS3 (and will crash FP9).

I stand by using addFrameScript as the best way to do this. It was undocumented for a long time (it may still be, in fact), but it's the way that the flash compiler internally attaches the script you write in the fla on the timeline to a movie clip's "frames".

0
votes

U can't access elements, which are on other frame than script/code itself. In this case, main class resides on first frame. So basicly best guide is that you put all elements on 1. frame and then hide/show them when needed.

But if u can't do that for any reason, my advice is, that on any frame that there is elements set some variable in main document, pointing to that element.

For instance:

on frame 2. you have "question_txt" element, and on that frame put code:

MovieClip(root).question_txt = question_txt

and in main class put variable question_txt:TextField :

public var question_txt:TextField;

And so on, for every frame and every element. U can also store them in array or dictionary or somethink similar, as long u can change the reference

0
votes

I tend to avoid using the document class and put an empty symbol on the stage with a base class and then treat that class like a document class.

0
votes

An object needs to exist on the stage for one whole frame before you can expect to have any sort of meaningful interaction with it via code. This is true always. When in doubt, wait one frame.