Flash CS5 reference a display object in the constructor of a non-document class
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 m开发者_如何学JAVAentioned 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
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 ...
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.
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
精彩评论