when does the stage become initialised? - actionscript-3

I have a singleton class that inherits from sprite so that it can access the stage, like this..
package
{
import flash.display.Sprite;
public class C extends Sprite
{
private var _grid:Array = new Array();
public function get Grid():Array
{
return _grid;
}
private static var _instance:C;
public static function get Instance():C
{
if (_instance == null)
{
_instance = new C();
}
return _instance;
}
function C()
{
this.InitGrid();
}
private function InitGrid():void
{
var gridWidth:Number = stage.width / 10;
}
}
}
This throws the error
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at C/InitGrid()
at C()
at C$/get Instance()
at C()
at Main()
If I replace stage.width with an int the code executes OK.
is this because the object has not been added to the displayList of any children of the stage?
Thanks

Yes. The Sprite will only have a stage property once it's a part of the Display list.
To get the stage you will need to either give your singleton a reference to the stage or add it to the Display list. If you choose the latter you can add a listener Event.ADDED_TO_STAGE, and handle that accordingly inside your singleton.

Related

AS3 Access a method inside the Main class from another class . Gives error?

I have trouble with actionScript , im trying to use a simple one line code to access a method inside the Document Class (Main) , but every time i got error . i tried the same code with a movieClip on stage, it work nicely .
Main Class linked to the fla :
package {
import flash.display.*;
import flash.events.*;
public class Main extends MovieClip {
public function Main() {
if (stage) {
init();
}
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
button.addEventListener(MouseEvent.CLICK,_click);
}
private function _click(e:MouseEvent):void {
var l:Leecher = new Leecher();
l.leech();
}
public function callMe():void {
trace("hey nice work");
}
}
}
Leecher Class :
package {
import flash.display.*;
public class Leecher extends MovieClip {
public function leech():void
{
trace(" leech function ");
Main(parent).callMe(); // output null object
Main(root).callMe(); // output null object
Main(Main).callMe(); // output null object
}
}
}
The Same code , but the class linked to a button on stage
package
{
import flash.display.*;
import flash.events.*;
public class Button extends MovieClip {
public function Button() {
this.addEventListener(MouseEvent.CLICK,r_click);
}
private function r_click(e:MouseEvent):void {
var l:Leecher = new Leecher();
l.leech();
Main(parent).callMe(); // hey nice work
Main(root).callMe(); // hey nice work
Main(Main).callMe(); // output null object
}
}
}
The errors are because when that code runs, the Leecher instance has not yet been added to the display list, and as such does not have a parent or root or stage (so parent is null).
Here is a breakdown of what's happening (explained with code comments):
private function _click(e:MouseEvent):void {
//you instantiate a new Leecher object
var l:Leecher = new Leecher();
//you call leech, but this new object does not have a parent because you haven't added it to the display list (via `addChild(l)`)
l.leech();
}
//your saying parent should be of type Main, then call the callMe method. However, parent is null because this object is not on the display list
Main(parent).callMe();
//same as above, except using root
Main(root).callMe();
//Here you are saying the Main class is of type Main (which since Main is a class and not an instance of Main will error or be null)
Main(Main).callMe();
The root, parent & stage vars of a display object are only populated when said display object is added to to the display list. In the case of root & stage the parent (and any grand parents) must also be added so that the top most parent/grandparent is the stage.
As a result, you need to wait until it's safe to access parent by listening for the Event.ADDED_TO_STAGE event.
private function _click(e:MouseEvent):void {
var l:Leecher = new Leecher();
//call the leech method once the child has been added to the stage and has a parent value
l.addEventListener(Event.ADDED_TO_STAGE, l.leech, false, 0, true);
addChild(l);
}
If you do the above, you'll need to add an optional event parameter to the leech method or you'll get an error:
public function leech(e:Event = null):void
{
To make your Main class easily accessible, you could use a static reference.
Static vars are not tied to an instance of an object, but to the class itself.
public class Main extends MovieClip {
//create a static var that holds a reference to the root/main instance
public static var main:Main;
public function Main() {
//assign the static var to this (the instance of Main)
main = this;
//...rest of code
If you do that, you can asses your root anywhere in your code by doing Main.main so in your example you could then do:
Main.main.callMe();
I'd recommend reading about static vars more before going crazy using them. Doing what I've just shown for an easy reference to your document class / root is safe, but in other contexts there are some memory & performance nuances it's best to be aware of.

AS3 | 1120: Access of undefined property stage

My goal is to create rectangle as MovieClip with stage size, but Flash gives me this error: 1120: Access of undefined property stage. (on line 6,7,14)
My code:
package {
import flash.display.MovieClip;
public class main {
var mc_background:MovieClip = new MovieClip();
var stageW:Number = stage.stageWidth;
var stageH:Number = stage.stageHeight;
public function main() {
drawBackground();
}
public function drawBackground():void {
mc_background.beginFill(0xFF00CC);
mc_background.graphics.drawRect(0,0,stageW,stageH);
mc_background.graphics.endFill();
stage.addChild(mc_background);
}
}
}
i had a similar problem, the thing is, the stage hasn't really been setup yet, so you need to wait to get data from it or stuff in it. just add this:
protected function addedToStageHandler(event:Event):void
{
//do stuff
}
protected funcion init():void
{
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
//more stuff
}
hope it helps
The stage property of an object isn't defined until the object has been added to the Stage or another object on the Stage.
The constructor of a class is called when the class instance is created, and that is before the instance could have been added to the Stage. So, you can't access stage within code you call from the constructor, or when you define the instance variables stageW and stageH.
To access the stage property as soon as the object is added to the stage, allow the object to handle the ADDED_TO_STAGE event:
package {
import flash.display.MovieClip;
import flash.events.Event;
public class main
{
var mc_background:MovieClip = new MovieClip();
public function main()
{
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(event:Event):void
{
// Generally good practice to remove this listener from the object now because it stops addedToStageHandler from being called again if the object is removed and added back to the stage or display list.
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
drawBackground();
}
private function drawBackground():void {
mc_background.beginFill(0xFF00CC);
mc_background.graphics.drawRect(0,0,stage.stageWidth, stage.stageHeight);
mc_background.graphics.endFill();
addChild(mc_background);
}
}
}

AS3 reference custom property in a custom class for a library symbol

So I've done a fair amount of searching through SO and couldn't quite find the answer to this question. I have a movieclip in my symbol library that's exported for actionscript, and I've written a custom class for it. It mostly works great except for when I try to access a custom private property after I've added the movieclip to the stage. Below's an example:
package {
public class MyMovieClip extends MovieClip {
private var _isEnabled:Boolean = false;
public function MyMovieClip():void {
trace(this);
}
public function set isEnabled( b:Boolean ):void {
_isEnabled = b;
}
public function get isEnabled():Boolean {
return _isEnabled;
}
}
}
And then I have another class where I am adding instances of the movieclip to the stage in a loop:
package {
public class MyOtherClass extends MovieClip {
public var myMC:MyMovieClip;
public var docClass:*;
public function MyOtherClass( docRef:* ):void { // passing in a reference to the DocumentClass so I can access the stage
docClass = docRef;
init();
}
public function init():void {
for(var i:int=0; i<6; i++) {
var myMC:MyMovieClip = new MyMovieClip; // instantiate the movieclip which is exported for actionscript and has a custom class
//set a few native properties
myMC.name = "myMC" + i; //setting the name so I can reference this movieclip after it's been added to stage
myMC.y = myMC.height * i + 20;
myMC.x = 20;
myMC.alpha = .7;
}
dispatchEvent(new Event(MyOtherClass.MOVIECLIPS_ADDED)); // just to be safe, let's dispatch a custom event when all movieclips have been added
}
public function traceEnabled():void {
trace(docClass.stage.getChildByName("myMC1").isEnabled); // this throws: 1119: Access of possibly undefined property isEnabled through a reference with static type flash.display:DisplayObject
}
}
}
And finally I instantiate MyOtherClass inside my document class:
package {
public class DocumentClass extends MovieClip {
public var myOtherClass:MyOtherClass;
public function DocumentClass():void {
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
public function onAddedToStage(e:Event):void {
myOtherClass = new MyOtherClass(); // upon instantiation, init is called in MyOtherClass and all of my movieclips are added to the sage
}
}
}
What gives? Why can't I access the MyMovieClip property, isEnabled, after it's been added to the stage? Is there another way? (Thanks in advance for any help)
Internally all children of a DisplayObjectContainer are referenced as DisplayObject, so when you use getChildByName, it returns a DisplayObject.
In order to access your custom properties without causing a compile-time error, you would need to cast the result of getChildByName as the Class of your custom properties. See the code below.
That however isn't your only issue (though it's the reason for the error, once you correct you will get runtime errors as well).
In your creation loop, your not adding myMC to the display list, so calling stage.getChildByName() will return null because your clips aren't on the stage.
Your also not adding your myOtherClass to the display list in the posted code.
Also, storing a reference to the document class isn't really needed. Just add the addedToStage listener in MyOtherClass and have the handler be init.
HERE IS SOME UPDATED CODE
For your MyOtherClass:
public function MyOtherClass():void {
if(stage){
init(); //if stage is ready, call init, if not wait for the added to stage event
}else{
addEventListener(Event.ADDED_TO_STAGE,init);
}
}
public function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE,init);
for(var i:int=0; i<6; i++) {
var myMC:MyMovieClip = new MyMovieClip;
myMC.name = "myMC" + i; //setting the name so I can reference this movieclip after it's been added to stage
myMC.y = myMC.height * i + 20;
myMC.x = 20;
myMC.alpha = .7;
addChild(myMC); //!!!! add to the displayList
}
dispatchEvent(new Event(MyOtherClass.MOVIECLIPS_ADDED)); // just to be safe, let's dispatch a custom event when all movieclips have been added
}
public function traceEnabled():void {
var myMC:MyMovieClip = this.getChildByName("myMC1") as MyMovieClip; //!!! cast it as MyMovieClip so you have access to all the properties/methods in that class
if(myMC){ //myMC will be null if the cast failed
trace(myMC.isEnabled);
}
}
/*
getChildByName is slow and cumbersome. Most people generally only use it for accessing things put on the timeline in the Flash IDE. Using events is a much better way of accessing your items. If traceEnabled was caused by a mouse event attached to myMC, then this would be a much better implementation:
*/
public function betterTraceEnabled(e:Event):void {
var myMC:MyMovieClip = e.currentTarget as MyMovieClip;
if(myMC){
trace(myMC.isEnabled);
}
}
AND YOUR DOCUMENT CLASS:
public class DocumentClass extends MovieClip {
public var myOtherClass:MyOtherClass;
public function DocumentClass():void {
if(stage){
onAddedToStage(null); //most of the time stage is already populated in the constructor of your document class
}else{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
}
public function onAddedToStage(e:Event):void {
myOtherClass = new MyOtherClass(); // upon instantiation, init is called in MyOtherClass and all of my movieclips are added to the sage
addChild(myOtherClass); //add it to the displayList
}
}

AS3 Cannot access stage from custom class

How can I access the stage and especially the width and mouse position of the flash Movie from a custom class?
package classes
{
import flash.events.*;
import flash.display.*;
public class TableManager extends Sprite
{
public function TableManager() {
sayStage();
}
public function sayStage():void
{
trace(stage);
}
}
}
This will only return nill. I know that DisplayObjects don't have any stage until they have been initiated so you can't access the stage in your constructor but even if I call sayStage() later as an instance method it won't work.
What am I doing wrong?
If TableManager is on the stage you can access the stage with this.stage.
The trick is you have to wait for the instance to be added to the stage. You can listen for the ADDED_TO_STAGE event so you know when that's happened.
package classes {
import flash.events.*;
import flash.display.*;
public class TableManager extends Sprite {
public function TableManager() {
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void {
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
sayStage();
}
public function sayStage():void {
trace(this.stage);
}
}
}
The most defensive way to write this is:
public function TableManager() {
if(this.stage) init();
else this.addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
if(e != null) this.removeEventListener(Event.ADDED_TO_STAGE, init);
sayStage();
}
If the object is already on the stage at the time of initialization, then immediately call the init function with no arguments. If not wait until its been added to the stage. Then when the init function gets called, if it was called as the result of an event, then detach the event handler, and move along.
You can pass a reference of the root movieclip (i.e. the stage) to your custom class.
e.g.
package classes
{
import flash.events.*;
import flash.display.*;
public class TableManager extends Sprite
{
private var _rootMC:MovieClip;
public function TableManager(rootMC:MovieClip) {
_rootMC = rootMC;
sayStage();
}
public function sayStage():void
{
trace(_rootMC.stage);
trace(_rootMC.stage.stageWidth);
}
}
}
Then when instantiating your instance of TableManager from the root timeline:
//the keyword 'this' is the root movieclip.
var newTM:TableManager = new TableManager(this);
stage will be null as long as the Sprite hasn't been added to the display list - it's nothing to do with initiation. E.g.
var t:TableManager = new TableManager;
stage.addChild( t ); // or your root class, or any class that's already on the displaylist
trace( t.stage ); // [Stage stage]
t.parent.removeChild( t );
trace( t.stage ); // null
As #crooksy88 suggests, either pass in the stage to the constructor, or keep it as a static somewhere, say your main document class, so that you can access it everywhere.
i think usefull for You should be create static reference to stage :
in Your main class add line and set stage :
public static var stage:Stage;
...
public function Main():void { // constructor
Main.stage = stage;
...
and than in custom class :
public function sayStage():void
{
trace(Main.stage);
trace(Main.stage.stageWidth);
}
you may access this.stage when the current object(also a sprite) is already attached to the stage.
public class TableManager extends Sprite{
public function TableManager()
{
}
public function sayStage():void
{
trace(stage);
}
}
TableManager tm=new TableManager();
//tm.sayStage(); // no
addChild(tm);
tm.sayStage(); // fine
hope this could help
here is a pretty good solution you only need to reference the stage inside your class you just pass it as a simple object, here how to do that
package {
public class Eventhndl{
private var obj:Object;
public function Eventhndl(objStage:Object):void{
obj = objStage;
trace(obj); // now the obj variable is a reference to the stage and you can work as normal you do with stage (addChild, Events Etc..)
}
}
this is how you make instance to run it, i have used the constructor method but you can change it to any function as you wish and call it whenever you need it.
import Eventhndl;
var EH:Eventhndl = new Eventhndl(stage);
here is some few Examples how to access stage from class
https://stackoverflow.com/a/40691908/1640362
https://stackoverflow.com/a/40691325/1640362

Accessing main class stage event listener

I wanted to remove an event listener from main class stage, but i get the error 1120: Access of undefined property stage. How do I actually access the stage?
custom class:
import main;
main.disableVcam();
main class:
public static function disableVcam():void {
trace("disable");
stage.removeEventListener(MouseEvent.MOUSE_MOVE, movevC);
}
Unless the object is on the display stage, the stage object will be undefined (or null). You have to addChild the object for the stage object to have a value.
Edit: Perhaps you can handle this in the event handler?
protected function clickHandler(e :Event) :void {
if (e.target.stage) {
e.target.stage.removeEventListener(...);
}
}
Edit2: Static methods don't have a stage, so to solve your problem you can make your Main-class a singleton, and work like this:
public class Main {
static private var instance :Main;
static public function getInstance() :Main {
if (Main.instance == undefined) {
Main.instance = new Main();
}
return Main.instance;
}
// The rest of the class goes here
}
// snip
import Main;
public static function disableVcam():void {
trace("disable");
Main.getInstance().stage.removeEventListener(MouseEvent.MOUSE_MOVE, movevC);
}
If your Main-class is the main class of the project, you need to assign the static instance variable's value in the constructor.