1

After learning of this excellent method of accessing an object placed on the stage in Flash CS5 in a class other than the document class (found in this thread), I have run into a stumbling point. I am using

this.stage.getChildAt(0).greenLight1.visible=false;

to turn a symbol with the instance name greenLight1 invisible when accessing greenLight1 (the instance name specified in my .fla for the bitmap). This works perfectly where I was using it which is in a sub function of a few different classes. The problem I'm running into is that you can't use this in the constructor of these non-document classes, or in any function called directly by the constructor. If I try to do that, I get the following "Error #1009: Cannot access a property or method of a null object reference."

So I figure that I'm not accessing this correctly and start to play with the phrase. When I switch it to

this.getChildAt(0).greenLight1.visible=false;

I get a different error "1119: Access of possibly undefined property greenLight1 through a reference with static type flash.display:DisplayObject." So I'm obviously missing something here.

Now I tried a different tactic that I found in this thread which I also mentioned in the above thread link I set up. And thanks to Allan's comment in my other thread I actually got that method working. However, when I try to place that reference into the constructor class of any other non-document class I actually get the same "Error #1009: Cannot access a property or method of a null object reference." But it works just fine if I use it in a sub function. So same problem it seems.

My project consists of a Game.fla file that holds various bitmap symbols and several buttons that I've placed into my Library and dragged to the stage (and I've given them all unique instance names). I've set up my document class Game.as and inside of it, I can access all of these items without fail simply using their instance name. Now in my sub class called Player.as, I've set up a function called lightsOut() which turns the lights off using a switch/case. It is there that I'm using "this.stage.getChildAt(0).greenLight1.visible=false;" and it is working perfectly. This is also where I set up the different tactic I mentioned above and tried "Game.GL1.visible=false;" and that worked perfectly as well. But then in my sub class called Controller.as where I have all of my mouse and keyboard handlers, I tried to set up a event listener for one of my stage buttons. So I placed it in the Controller constructor function where I have all of my other event listeners set up and already working (they use "stage.addeventlistener...") and it was here that I started to see errors. So I figured I would try to just get the greenLight1 to turn off since I already knew that code was working and tried both the "this.stage.getChildAt...." line and the "Game.GL1..." line only to watch them cause the errors I mentioned above. So I went back to my Player.as class and tried them in it's Player constructor class and once again got the same errors even though I'm using the same line lower in the Player.as class just fine. Note that I have passed the stage to both Controller and Player and have been using it just fine in both. I also tried setting up a call to another function in the constructor called Init(); and got the same error when trying to access greenLight1 in there as well, yet it still worked in lightsOut(). Also not sure if it matters, but all classes extend MovieClip as well. I don't know if this will help but here is the beginning of the Player class along with the lightsOut function I've stripped out the rest of the code as it's unrelated:

package {
import flash.events.Event;
import flash.display.MovieClip;
import flash.display.DisplayObject;

public class Player extends MovieClip {
    private var _stage:Object;
    private var _lights:uint;

    public function Player(stage:Object):void {
        this._stage = stage;
        this._stage.getChildAt(0).greenLight1.visible=false; //errors
        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }
    private function lightsOut():void {
        switch(_lights) {
            case 1:
            this._stage.getChildAt(0).greenLight1.visible=false;//works
            break;
            case 2:
            this._stage.getChildAt(0).greenLight2.visible=false;//works
            break;
            case 3:
            this._stage.getChildAt(1).greenLight3.visible=false;//works
            break;
        }
    }

What am I missing here?

EDIT: OK here is the document class (at least what is important to Controller and Player):

public class Game extends MovieClip {
    private var _player:Player = new Player(stage);
    private var _controller:Controller = new Controller(_player, stage);

    public function Game():void {
        addChild(_player);
        addEventListener(Event.ADDED_TO_STAGE, added);

Here is the important part of the Controller class and I added a comment to what is failing (the real reason I started this post in the first place):

public class Controller extends MovieClip {
private var _stage:Stage;
private var _model:Object;

public function Controller(model:Object, stage:Stage):void {
    this._model = model;
    this._stage = stage;

    _stage.addEventListener(KeyboardEvent.KEY_DOWN, processKeyDown);
    _stage.addEventListener(KeyboardEvent.KEY_UP, processKeyUp);
    _stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
    this._stage.getChildAt(0).fireButton.addEventListener(MouseEvent.CLICK, onClick); //this is what isn't working
    this._stage.getChildAt(1).greenLight1.visible=false; //added this for testing

That last line I only added to see if I could get greenLight1 to go invisible like I can when I call it in the lightsOut() function I noted in Player.as. But it does NOT work either when placed here in the Constructor. HELP!!!! lol

Community
  • 1
  • 1
FlashNoob468
  • 175
  • 1
  • 17
  • Can we see some more code of how you are setting things up? :) Are you using a document class? Where are you creating these non document classes? – Allan Jul 29 '11 at 05:28
  • @Allan I can't add code because my program is already hundreds of lines of code long and really a mess considering I was trying to get it to work in the document class and then slowly move parts out to sub classes a bit at a time. It's been working just fine until these complications. I did, however, update my information above adding a paragraph at the end describing my information as best as I can without code. I hope this helps? – FlashNoob468 Jul 29 '11 at 06:08
  • @Allan OK I take it back... I added some code. Does that help? – FlashNoob468 Jul 29 '11 at 06:33
  • Yeah that does help :) see my answer to see how that goes. Also, if it doesn't try tracing the stage in the constructor to make sure it is not null. – Allan Jul 29 '11 at 06:40
  • So as I understand it your issue is now centered around the Controller class, is that correct? It might help if you could post that too. Does the controller get attached to the stage at any time, and where is it created? – shanethehat Jul 29 '11 at 15:20
  • @shanethehat OK I added the beginning of the document class Game.as and I added the beginning of the Controller class. I call controller in the document class and pass it the stage there as well. Everything about it is working perfectly except when I try to access the fireButton that I mentioned in my original post (which is a button that I dragged from the library to the stage and gave the instance name fireButton). – FlashNoob468 Jul 29 '11 at 16:17

3 Answers3

1

The fact that you can not access the instances on the stage in a class' constructor but can in another function of the class (that you call later I assume) makes me think that stage is not available to your constructor when it is being called.

Check to make sure that your Game.as document class is set up like this, although it probably is since you followed my method:

public function Game() {
    addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
    //DO NOT CREATE CLASS INSTANCES IN HERE
}

private function init(e:Event):void{
    //In here is where we create instances of classes
}

Additionally, set your stage data type to Stage instead of Object in your Player class's constuctor's paramater for good coding practice.

Allan
  • 3,339
  • 2
  • 21
  • 24
  • I got the error "1046: Type was not found or was not a compile-time constant: Stage." when I changed stage:Object to stage:Stage. I also changed the var _stage:Object to var _stage:Stage as well. Did I miss an import or something? – FlashNoob468 Jul 29 '11 at 06:50
  • Sounds like it. Make sure you have the import flash.display.Stage; – Allan Jul 29 '11 at 06:52
  • That actually made things worse. I lost the ability to use "this._stage.getChildAt(0).greenLight1.visible=false;" everywhere. I now get a bunch of the error 1119's on every call in the class when I set stage:Stage. I commented everything out so I could trace(stage); and it returned [object Stage]. Is that what I'm supposed to get? – FlashNoob468 Jul 29 '11 at 06:59
  • OK I just set up your method again and when I use it in lightsOut() it works fine when I have stage:Stage (this._stage.getChildAt... still doesn't work anywhere but your method does). I'm now using "Game.GL1.visible = false;" to turn off greenLight1 and it works but ONLY in lightsOut(). If I move that same line up to the Player constructor I get "#1009: Cannot access a property or method of a null object reference." ....... Maybe some sleep will help. lol. – FlashNoob468 Jul 29 '11 at 07:12
  • Yeah that is what you are supposed to get (Stage being the type). So we know that indeed the actual stage is being passed around. Hmm I don't have anymore time to look into it today. I will have a look again tomorrow unless someone else figures it out. – Allan Jul 29 '11 at 07:15
  • Try setting some breakpoints and step through the debugger too. Keep an eye out for null values. – Allan Jul 29 '11 at 07:16
1

The problem that you're having is that you are instantiating your controller class before you have a stage object to pass to it. You must wait until after the ADDED_TO_STAGE event is fired to instantiate everything else:

public class Game extends MovieClip {
    private var _player:Player;
    private var _controller:Controller;

    public function Game():void
    {
        addEventListener(Event.ADDED_TO_STAGE, added);
    }
    private function added(evt:Event):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, added);
        _player = new Player(stage);
        _controller = new Controller(_player,stage);
        addChild(_player);
    }
}

There is a better way

You do not need to pass stage though to your player and controller. If you just pass a reference to this, you will pass a reference to the document class. This is a better practice to get into as it prepares you for more advanced programming approaches like design patterns.

So you would have your document class:

public class Game extends MovieClip {
    private var _player:Player;
    private var _controller:Controller;

    public function Game():void
    {
        addEventListener(Event.ADDED_TO_STAGE, added);
    }
    private function added(evt:Event):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, added);
        _player = new Player(this);
        _controller = new Controller(_player,this);
        addChild(_player);
    }
}

and in your player class:

public class Player extends MovieClip {
    private var docRef:Game;
    private var _lights:uint;

    public function Player($docRef:Game):void {
        this.docRef = $docRef;
        docRef.greenLight1.visible=false; //no longer needs to wait for player to be on the stage
    }
}

The advantage is obvious, since you know that you document class is on the stage, Player does not need to be on the stage before it can interact with it. It does not need ever be on the stage. If you need stage, say for adding listeners like you do in the Controller, you can use the documents stage property:

docRef.stage.addEventListener ...
shanethehat
  • 15,460
  • 11
  • 57
  • 87
  • I didn't mean to sound condescending when I mentioned design patterns. What you have here (if you follow my second approach) is coming very close to a Model-View-Controller pattern. – shanethehat Jul 29 '11 at 17:39
  • Shanethehat that was PERFECT once again. You are my hero lol. That is a simple to execute design that is working like a charm. Thank you SO so much. This has dead locked me for almost 2 days now. I can finally get back to trying to get this game rocking. Till the next hurdle! – FlashNoob468 Jul 29 '11 at 17:40
  • Not at all condescending, I actually am using a how-to-code book that was written in MVC to help me design this game. The problem is... I HATE MVC. It just doesn't make logical sense to me to write 2 to 3 times the amount of code to make things work. To add something to the stage I have to add the model, the view and maybe the controller? Why not just add the thing and it's either visible or not. Granted I'm just an uberNoob but I just can't find the logic in working that way. However, I do have to admit that things like this above do work very well, so it can't be all that bad. Thanks again! – FlashNoob468 Jul 29 '11 at 17:50
  • The only thing that goes near the stage should be the view. The modal is just a data store, and as data changes the controller acts upon it. The beauty comes later, when you start reusing and extending bits of your code. And remember that MVC is just one pattern, there are many others and they all have different purposes. http://www.amazon.co.uk/Design-patterns-elements-reusable-object-oriented/dp/0201633612/ref=sr_1_1?ie=UTF8&qid=1311962161&sr=8-1 – shanethehat Jul 29 '11 at 17:57
  • OK I went one step further and need a bit more guidance please. I now have a new document class called Main.as and its purpose is to call all of the various different screens and display them on the stage. So I set up Main.as passing the stage using "this" (as Game used to). In Game.as I changed it to get the stage via $docRef:Main. Now, however, it's still Game's job to call Player, so do I pass the stage using docRef like _player = new Player(docRef);? And then in Player do I still use $docRef:Main or do I now use something like DocRef:Stage? How do I take this one level deeper? – FlashNoob468 Aug 02 '11 at 20:13
  • Exactly how you think. `_player = new Player(docRef);` to create the player, and in doing so you are passing a _reference_ to the Main class so in Player it retains the type `Main`. No matter how many times you pass that reference it must retain its original type or things stop working, and you can pass it though as many depths as you like. – shanethehat Aug 02 '11 at 20:18
  • I took everything (buttons, GreenLights, etc.) and put them into a symbol called ControlPanel (with Export for ActionScript checked) and then did an addChild(_controlPanel) to get it too appear on the stage, but docRef.greenLight1.visible had stopped working. But I changed it to docRef._controlPanel.greenLight1 and it didn't error. But now I get TypeError: Error #1009: Cannot access a property or method of a null object reference. at ControlPanel() at Main/added(). The only thing in Main/added() is `_controlPanel = new ControlPanel(this);` `addChild(_controlPanel);` does it not like "this"? – FlashNoob468 Aug 02 '11 at 20:51
  • Can you open this as a new question, cutting away the bits that aren't relevant? So definitely show when you create _controlPanel (both definition and instance creation), and what the ControlPanel constructor looks like. – shanethehat Aug 02 '11 at 20:54
  • Yeah I was toying with the idea of that before you mentioned it. Set [it up here](http://stackoverflow.com/q/6918852/868368) – FlashNoob468 Aug 02 '11 at 21:29
0

stage is a property of DisplayObject only accessible/defined once it has been added to the DisplayList (ie - added to something via addChild())

You won't have any luck accessing stage from a class constructor unless it's the document class.

As mentioned, you can shift the contents of your constructor to a custom function which is called once Event.ADDED_TO_STAGE has been triggered.

For example, here's a demo class:

public class Thing extends DisplayObject
{
    /**
     * Constructor
     */
    public function Thing()
    {
        // Add listener
        addEventListener(Event.ADDED_TO_STAGE, _added);
    }

    /**
     * Called once this has been added to the display list
     * @param e Event.ADDED_TO_STAGE
     */
    private function _added(e:Event):void
    {
        // Discard listener
        removeEventListener(Event.ADDED_TO_STAGE, _added);

        // My initial code
        trace(stage + " is accessible");
    }
}

Which we then create an instance of:

var thing:Thing = new Thing(); // nothing happens

And then add to the stage/current container:

addChild(thing); // output: [Object Stage] is accessible

As for accessing greenLight1, this should be achievable easily via either of the following:

stage.greenLight1;
stage["greenLight1"];

Also, you might want to replace:

public function Player(stage:Object):void

With:

public function Player(stage:Stage):void
Marty
  • 39,033
  • 19
  • 93
  • 162
  • When in the document class, greenLight1 is accessible just by doing "greenLight1.visible=false;" but in Player.as stage.greenLight1 or stage["greenLight1"] (as well as _stage when I pass it like I show above) all give me the error "1120: Access of undefined property greenLight1." I set up var player and addChild(player) in my document class like you had stated and I am still unable to change the visibility of greenLight1 in the Constructor of Player.as. Also I get the same 1120 error if I try to mess with it in the _added function too since it's called by the Constructor. – FlashNoob468 Jul 29 '11 at 14:28
  • Let me ask you this. When I use 'for(var i:int=0;i – FlashNoob468 Jul 29 '11 at 14:34