This, stage & parent are null - actionscript-3

Im trying to access the stage from an external class this is what I have:
Player.as (Main Class)
import flash.display.MovieClip;
public class Player extends MovieClip
{
private var _controls:Controls;
public function Player()
{
// constructor code
_controls = new Controls();
}
}
Controls.as
import flash.display.MovieClip;
public class Controls extends MovieClip
{
private var _playbtn:MovieClip;
public function Controls()
{
trace(this.getChildByName("playbtn"));
}
}
but this line trace(this.getChildByName("playbtn")); always errors I have even tried:
trace(stage.getChildByName("playbtn"));
trace(parent.getChildByName("playbtn"));
But I get the same error:
Null for this and
Cannot access a property or method of a null object reference. for
stage & parent
Is there something I am doing wrong?

I think you are mistaken as to what getChildByName actually does. It returns reference to an object with its name property set to the supplied value. It does not return a reference to an object set to a variable with the supplied name.
For a reference to be returned by getChildByName, you must instantiate an object, set its name to something, and then call addChild on a DisplayObjectContainer with that object. Then you can call getChildByName on the DisplayObjectContainer.
For future reference, stage will be null until the object is added to the stage. parent will be null until the object is used in an addChild call

Display objects get access to their 'stage' property when they are added to the stage. You haven't added the _controls to the stage ( eg addChild(_controls) ) when you try to access the stage property.
Add an event listener (Event.ADDED_TO_STAGE) to the constructor of your Controls class that points to a handler that then checks the stage property.

If you are adding everything to the stage manually, there is no need to create a new instance of Controls in the constructor of Player, you should just retrieve the reference:
_controls = this.controls; //where 'controls' is the instance name you assigned in Flash IDE.
Also, where is the 'playbtn' instace you are trying to retrieve? Is it on the stage or is inside of the Controls instance? As you are tracing this.getChildByName("playbtn"); it HAS TO be inside of Controls with the instance name playbtn assigned in the IDE (or via .name property).
You are getting indeed a null for parent and stage because you have not added the instance to the stage. What you are doing right now:
You instantiate Player, the player then instantiates the Controls (it does not retrieve the reference you have on stage, because you are calling it with the new keyword!)
The new controls have never been added to anything, therefore they have no parent and neither stage.

Related

As3 Adding MC from Libary and accesing content inside loaded MC

I have a movieClip I am loading from the Libary and I have properly LINKED it to export with a name of myMC. This movieclip contains another movieClip and some properties. Lets call the movieClip inside: insideMC.
Here is my code:
function loadScreen()
{
var newMC:MovieClip = new myMC();
addChild(newMC);
loadButtons();
}
function loadButtons()
{
newMC.insideMC.addEventListener(MouseEvent.CLICK, homeButtons);
}
loadScreen();
HOWEVER, when I call the function loadButtons() within the loadScreen() function then I get this error.
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at iRosary_fla::MainTimeline/loadButtons()[iRosary_fla.MainTimeline::frame1:83]
at iRosary_fla::MainTimeline/loadScreen()[iRosary_fla.MainTimeline::frame1:110]
at iRosary_fla::MainTimeline/frame1()[iRosary_fla.MainTimeline::frame1:103]
It is not seeing the insideMC. Perhaps because it's calling to fast or not loaded yet. It is calling and loading the newMC tho. Just the function loadButtons() is not working because it is not seeing the insideMC movieClip. I am sure this is an easy fix but I can't find it anywhere. Thanks
newMC is a local variable in your loadScreen() method, therefore it has no scope in your loadButtons() method.
Declare newMC as a class member variable and it will have scope in loadButtons()
for example :
// in class declarations
public var newMC:MovieClip;
function loadScreen()
{
newMC = new myMC();
addChild(newMC);
loadButtons();
}
It's important to understand that :
var newMC:MovieClip = new myMC();
Creates a local variable. From your comments, it sounds like you did have newMC as a class variable. So you assumed that the above line was assigning the new instance to your class member newMC, and not the local variable you created.
Not completely sure this is your problem. But to access a movie clip within a movie clip you have to give that "insideMC" an instance name within the first movie clip. Otherwise you'll reference an object that you haven't added to the stage - a null object.
Tutorial on instance names here

Stage and classes

I am new to AS3 and am trying to lean its OOP ways. What I am having problems with is understanding how to access the stage with separate classes.
Here is an example of what I am trying to do:
package game{
import flash.display.*;
public class Main extends MovieClip{
function Main(){
var player = new Player();
var playerBullets = new playerBullet();
addChild(player.players);
}
}
package game{
import flash.display.*;
public class Bullet extends Main // also tried with MovieClip and Sprite{
function Bullet(){
// empty
}
function blah(){
var someSprite = new someSprite();
Main.addChild(someSprite);
stage.addChild(someSprite);
root.addChild(someSprite);
}
}
}
I have Omitted another class which calls the blah method as I feel it is not relevant.
Basically what I want to know is how to add things to the stage in classes as it lookes like I am missing something crucial.
*EDIT TO INCLUDE ERROR*
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at game::Bullet/blah()
at game::Player/fire()
You shouldn't necessarily be extending main to create something like a bullet class, this can be it's own class that extends Sprite or MovieClip. The stage object is considered a global object, as it is a singleton (except in the case of Adobe AIR where you can have one stage per NativeWindow that you spawn). So any object that extends DisplayObject or has DisplayObject in it's inheritance chain will by default have a reference to the stage via a getter, which is populated automatically when a displayObject is added to the display list. This can happen by either adding a clip directly to the root stage object or by adding a clip as a child of another clip, that eventually connects to the stage. For example:
var clip1:MovieClip = new MovieClip();
stage.addChild(clip1); //Clip 1 can now access the stage reference internally.
ver clip2:MovieClip = new MovieClip(); //Right now, clip2 cannot access the stage reference interally.
clip1.addChild(clip2); //Now clip2 can access the internal stage reference because it has been connected to the display list through clip1.
The other mistake people make is accessing stage within a DisplayObject typed class (such as your Main class) without first ensuring that the object itself has been added to the stage. You do this by listening for the Event.ADDED_TO_STAGE event within the constructor of the class, like so:
public class Main extends MovieClip{
function Main(){
if(stage){
//The stage reference is present, so we're already added to the stage
init();
}else{
addEventListener(Event.ADDED_TO_STAGE, init);
}
var player = new Player();
var playerBullets = new playerBullet();
addChild(player.players);
}
private function init(e:Event = null)
{
trace("Added to stage, the stage reference is now populated and stage can be accessed");
}
}
This could be the problem you're having, but it's hard to say since you have not specified any errors. However, this is likely an issue or will be for you, since it's quite common. Inside the init() method you can then set a flag so that when external classes call your Main.blah() method, you can ensure that the stage reference exists before attempting to add something to the stage. Take note however that within your Main class when you simply say:
addChild(someChild);
or
this.addChild(someChild);
you're not adding that child to the stage, but rather to the Main object, which is a MovieClip or Sprite based object that is itself attached to the stage automatically when you set it as the Document class. Hope this info helps.
Update
To explain the display list a little more:
Think of all your movieclips as dishes, and the stage as the table. You can only access the table from the dish, if the dish is placed directly on the table, or if a dish is stacked on top of another dish that touches the table. If you have 10 plates stacked on top of each other, they all touch the table eventually, via their connection to each other. This is essentially a visualization of the flash display list. The way you put dishes on the table is by using addChild(dish). If you have not placed an object somewhere on the table, and try to access the table from that object, you're going to fail. You're getting the "access to undefined" error because you're calling the "blah()" method, which accesses the stage (table) before the bullet (dish) has been added to the stage (table). So you must first either directly add the bullet to the stage, or add it to another object that has already been added to the stage. Change your code like so:
var myBullet:Bullet = new Bullet();
stage.addChild(myBullet);
//Or, if this class, assuming it's the player class, has already been added to the stage, just do this:
this.addChild(myBullet);
myBullet.blah();
Even so, you should still have some error checking within your "blah" method to ensure that the stage is available:
function blah(){
var someSprite = new someSprite();
if(stage){
Main.addChild(someSprite);
stage.addChild(someSprite);
root.addChild(someSprite);
}else{
trace("error, stage not present");
}
}
However you should also note that by adding this child to Main, then stage, then root all in sequence, this does not duplicate the someSprite object. When you add a display object to a new parent object, the object is automatically pulled from it's current parent and moved to the new one. So all this code will do is eventually add someSprite to root, which I believe will fail because root is not a display object, but rather a global reference mainly used to access global objects such as the stage and the Loader object used to load the SWF.
You shouldn't ever be calling stage.addChild. There should be only one child of the Stage, and that's the document class.
You make a MovieClip display on the screen by adding it to the stage's display list.
Stage
+ Main Timeline
+Anything
+Else
+You
+Want
So assuming that Main is your document class for the main timeline...
// inside of Main's constructor...
public function Main(){
var anything:MovieClip = new MovieClip();
var Else:TextField = new TextField();
var you:SimpleButton = new SimpleButton();
var want:Sprite = new Sprite();
this.addChild(anything);
this.addChild(Else);
this.addChild(you);
this.addChild(want);
}
Then in order to add children even lower, for example if you want something to be a child of "Anything" such that you have....
Stage
+ Main Timeline
+Anything
+And
+Everything
+Else
+You
+Want
public function Main(){
var anything:MovieClip = new MovieClip();
var Else:TextField = new TextField();
var you:SimpleButton = new SimpleButton();
var want:Sprite = new Sprite();
this.addChild(anything);
this.addChild(Else);
this.addChild(you);
this.addChild(want);
var And:Sprite = new Sprite();
var everything:Sprite = new Sprite();
anything.addChild(And);
anything.addChild(everything);
}
EDIT: Ascension Systems asks why you should never add any display object directly as a child of the stage. The simplest answer is that you can't ever guarantee that what you believe you're creating as a document class, or as a main timeline in fact actually is going to be used as such. Your use of the stage may later preclude your swf from being loaded as a child of a larger application depending on what it is you've done, exactly. Relying directly on the stage can mean that you're making some assumptions about the nature of the display list that may not hold in the future. That's the way in which it breaks modularity (which is not the same as breaking oop).
Why add to the stage when you could just create your entire application as a MovieClip that is completely self-contained with no reliance on the concept of a "stage" beyond that which is required for learning world coordinates? That way you can be much more modular in your design and you sacrifice nothing.
In some people's work this may be considered an edge case. In my work this has happened both to me when I've created applications that I thought at the time were purely stand-alone that ended up being repurposed later to be a module, and also to swfs that other people created that were intended to be strictly stand-alone, but that I was then to integrate as a module into a larger application. In all cases there were some nasty side effects to contend with. That's where I learned not to rely too closely on the stage for much beyond world coordinates.
Every display object has a property called stage, which is null until that object is added to the display tree.
When you are unsure if an object has been added to the stage, there is a listener you can employ for that purpose:
public class Main extends MovieClip
{
import flash.events.Event;
public function Main():void
{
if(stage) {
init();
} else {
this.addEventListener(Event.ADDED_TO_STAGE,init);
}
}
private function init(evt:Event = null):void
{
this.removeEventListener(Event.ADDED_TO_STAGE,init);
//object is now definitely on the display tree
}
}
I'm gonna take a wild stab in the dark here.
stage is a property implemented something like so:
public function get stage():Stage {
var s:DisplayObject = this;
while(s.parent) s = s.parent;
return s as Stage;
}
root is very similar but stops a level below stage (root is a child of stage).
These properties only work when the object you're calling them on is on the stage somewhere. Doesn't matter where, because the while loop will walk up the hierarchy to get to the stage node at the top. But if it's not on the stage, then parent will be null.
So if your movieclip is not on the stage, then its reference to stage will be null. Same goes for root.
I'm guessing that you're calling blah before the bullets are added to the stage? In which case your call stage.addChild(someSprite) will be a Null Reference error (stage is null).
So you either need to add the bullets to stage first, or you need to pass stage in as a parameter:
function blah(s:Stage){
var someSprite = new someSprite();
s.addChild(someSprite);
}

Is it possible to load a non-Document Class before preloader starts?

public class Framework extends MovieClip
{
var _loadingSystem:LoadingSystem;
public function Framework()
{
_loadingSystem = new LoadingSystem(this);
loaderInfo.addEventListener(ProgressEvent.PROGRESS,progressHandler);
loaderInfo.addEventListener(Event.COMPLETE, completeListener);
}
...
public class LoadingSystem extends MovieClip
{
public function LoadingSystem(parent:DisplayObjectContainer)
{
parent.addChild(this);
myLogo.buttonMode = true;
myLogo.addEventListener(MouseEvent.CLICK, gotoMySite);
}
As you can see, Framework is my Doc class which is creating _loadingSystem which is basically a movieclip that contains the preloader graphics. When I debug I get the following error "TypeError: Error #1009: Cannot access a property or method of a null object reference." pointing to myLogo.buttonMode = true;
From what I understand this is due to LoadingSystem not being fully loaded before being created in Framework. Is there any way for me to make this work? I have tried adding listeners for Event.ADDED but it didn't work.
Additional info: 3-frame FLA, first empty with a stop, second holding an AssetHolder movieclip, third for the application. I have export on 2nd frame set in publishing settings, all checkboxes for export on 2nd frame unchecked in the assets, and this all worked before I changed the export on 2nd frame setting except it wasn't preloading 50% of the file.
i think what's happening is this:
A document class is ALWAYS loaded in the first frame, because it represents your swf root class and thus has to be there in the first frame. Now, since you export all the other classes to frame 2, i would imagine, that LoadingSystem is existing only beginning with frame two, but you try to instantiate it in the constructor of your document class Framework.
What you could try out is, create a method "initialize" in Framework and call that from the timeline in frame 2. And in that method you would do the stuff, you currently do in the constructor of Framework.
if myLogo is a sprite/movieclip on the stage, it wont exist until LoadingSystem is added to the stage.
Now your first reaction should be "but I added it to the stage with parent.addChild(this)!". What you didn't take into account is that the document class isn't on the stage when the constructor is called. Flash basically executes like this:
docClass = new DocumentClass();
stage.addChild(docClass);
Which means that the stage property of the document class will be null until after the constructor is finished. It also means that any children added during the constructor wont have access to the stage or objects located on the stage until after the docClass is added to the stage.
There is a simple fix; listen for the ADDED_TO_STAGE event.
public function LoadingSystem(parent:DisplayObjectContainer)
{
parent.addChild(this);
addEventListener(Event.ADDED_TO_STAGE, initialize);
}
private function initialize(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initialize);
addEventListener(Event.REMOVED_FROM_STAGE, uninitialize);
//attach stage listeners etc
myLogo.buttonMode = true;
myLogo.addEventListener(MouseEvent.CLICK, gotoMySite);
}
private function uninitialize(e:Event):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, uninitialize);
addEventListener(Event.ADDED_TO_STAGE, initialize);
//detach stage listeners etc.
}

AS3 trouble instantiating Document Class of loaded SWF

I am loading one swf into another using the Loader class, but when the child swf finishes loading and is added to the display list, its Document Class is not instantiated. I have a few trace statements that should execute when the object is created, but nothing is happening when loaded into the parent SWF. When I compile the child SWF on its own, the Document Class runs as expected.
So I'm wondering... how do I associate a child SWF's Document Class with Loader.content?
Code updated with a solution from Kishi below.
public class Preloader extends Sprite {
import flash.net.*;
import flash.display.*;
import flash.events.*;
// code in parent SWF's Document Class (Preloader.as)
private var swfLoader:Loader;
public var mainMovie:MovieClip;
public function Preloader(){
swfLoader = new Loader();
swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderDone);
swfLoader.load(new URLRequest("mainmovie.swf"));
}
private function loaderDone(e:Event):void {
// Cast Loader.content to MovieClip
mainMovie = MovieClip(swfLoader.content);
mainMovie.addEventListener(Event.ADDED_TO_STAGE, mainMovieAddedListener);
addChildAt(mainMovie, 0);
}
private function mainMovieAddedListener(e:Event):void {
// init() not necessary
}
}
// MainMovie.as runs after casting swfLoader.content to MovieClip
public class MainMovie extends Sprite {
public function MainMovie(){
trace('MainMovie WHATTUP');
}
public function init():void {
trace('init');
}
}
Cheers!
The problem is how you are trying to access the swf instance.
First, the document class instance is referred by Loader's content property. You'd reference it like this:
var swf:DisplayObject = swfLoader.content;
But, even then you'd have to cast the DisplayObject either to it's real class (MainMovie, in this case) or a dynamic class (such as MovieClip), as you are trying to use a custom property, that's not part of DisplayObject itself. Therefore, you could call MainMovie.init() like so:
var swf:MovieClip = MovieClip(swfLoader.content);
swf.init();
Hope that helps.
You're adding the mainMovie to stage, then you add a listener which inites it. The event is not fired as it's added after the movie is on stage.
What Cristi said is probably right, for the reason your init method isn't firing, but the more strange issue is that your loaded child swf's Constructor, MainMovie, should be firing as soon as that object is created.
Whenever I've done things like this, I've never created a new Sprite object from the contents of the loaded swf. Seems what you're doing there is using the swf like it's BitmapData, to create the Sprite mainMovie.
Try this: remove your statement that says swfLoader = null, and instead say addChild(swfLoader);. If you still want that event listener checking for it being added to the stage, put it before you do the addChild(swfLoader);, and of course put it on the swfLoader not the mainMovie object:
swfLoader.addEventListener(Event.ADDED_TO_STAGE, mainMovieAddedListener);
addChild(swfLoader);
See what you get then. [also can you paste the exact error you got from trying to access init?]
debu

ActionScript 3.0 stageWidth in custom Class

How do I access Stage Class properties in Costum Class?
Class:
package {
import Main;
import flash.events.*;
import flash.display.Sprite;
import flash.display.Stage;
public class Run extends Sprite {
var obj:a1_spr;
public function Run() {
runAssets();
}
private function runAssets():void {
obj = new a1_spr()
addChild(obj);
obj.x = stage.stageWidth/2;
}
}
}
Output:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
To expand on what Joel said, and put it into context:
Every display object has a .stage property, but that property is null until you add you display object onto the display list. So during construction, you will never be able to access it, (because it gets added afterwards)
The event ADDED_TO_STAGE gets fired when you add your object to the stage, ltting you know that the .stage property is now populated. After that happens you can access the stage from anywhere in you object.
Hope that clarifies things for you.
this.addEventListener(Event.ADDED_TO_STAGE, handleAdedToStage)
private function handleAddedToStage(event:Event):void
{
this.runAssets()
}
private function runAssets():void
{
obj = new a1_spr();
addChild(obj);
obj.x = this.stage.stageWidth/2;
}
You aren't going to have access to the stage in the constructor (unless you inject the stage into the class). Sprite has a stage property.
when flash compiles the fla assets with your .as files, there's no stage. so the code is initiated as preparation for your documentclass, you have to listen to if there's a stage so it can be rendered.
that's why you listen to ADDED_TO_STAGE , to check it's actually in the display list.
This problem occurs for all display objects, since they must be added to the display list when there's an actual stage.
get used to add that listener, and check for a stage. specially when working in a team and your doing your own components in a larger project.