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

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.

Related

Access of undefined property issues in AS3

I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.

Object On Stage Cannot Be Accessed From External Class

I have an external ActionScript class, which is called Menu.as, trying to access objects on the stage using the following code:
MovieClip(parent).fullmenu_mc.x = 80;
Although the program compiles, I get Error #1009: Cannot access a property or method of a null object reference
I don't see how this is possible since the object is already on the stage and just needs to be accessed. Am I somehow accessing the object incorrectly?
I also tried the following code inside Menu.as:
import EngineClass;
var engine : EngineClass = new EngineClass();
engine.fullmenu_mc.x = 80;
which gives the same runtime error. Any thoughts are welcome!
Short Version:
Basically you first need to setup a variable that acts a reference name to your on-stage movieclip. You can instead make it a public static variable if you want other Class files imported to also control this variable (and therefore also the movieclip) from their own Class code.
So define a public static var menuMC : MovieClip = new MovieClip(); then when Stage + stage items is available (since Flash processes code first then makes a Stage + items after), we update definition to know the instance name of on-stage object it represents by saying: menuMC = DisplayObjectContainer(getChildByName("fullmenu_mc")) as MovieClip;
Now you can update via any imported external class using a line like: Main.menuMC.x = 200; (where Main is the name of Class that holds the definition of what menuMC means).
Long Version:
Assuming you have some setup like this.
fullmenu_mc = instance name of Object on stage
Main.as = your FLA's document Class file attached to hold the main running code
External.as = some other external Class file that will also control the
on-stage MC
Note: If your code is on the timeline instead of a (Main.as) document class let me know to update (if possible), but the above Classes setup would be easier...
In Main.as.. (define variable and also run a function that is inside external Class)
package
{
import flash.display.MovieClip;
import flash.display.*;
import flash.events.*;
import External;
public class Main extends MovieClip
{
public var ExtClass : External = new External; //init the external class
public static var menuMC : MovieClip = new MovieClip(); //reference to on-stage MC
public function Main ()
{
if (stage != null) { Init(); } //do Init function if SWF loaded ok...
else { addEventListener (Event.ADDED_TO_STAGE, Init); } //or do the function when ready..
}
//If stage is available then we can now do this
public function Init (event:Event = null) :void
{
//update menuMC to know it means some item "X" on stage. "X" is instance name
menuMC = DisplayObjectContainer(getChildByName("fullmenu_mc")) as MovieClip;
trace("menuMC is type : " + menuMC); //if ok then traces [object MovieClip]
menuMC.x = 10; //just for testing
ExtClass.accessTest(); //run some function in External class
}
}
}
Now in External.as we can control the fullmenu_mc (now known as menuMC) like so..
public function accessTest () :void
{
//access stage item using Static Variable from Main Class
Main.menuMC.x += 500;
trace("Full_menu... new x-pos is : " + Main.menuMC.x);
}

Reference MovieClip After it is Added to Stage as a Child

I am currently having problems referencing a MovieClip child which I add to the Stage from the Document Class. Basically when the MovieClip child is added to the Stage from the Document Class, I want a certain MovieClip already on the Stage to reference it once it is on the Stage.
Also, if it is possible, I don't want the MovieClip referencing the child being added to the Stage to have parameters linking it with the Document Class, because I plan on nesting this MovieClip within another MovieClip later on in the future.
Here is the code for the MovieClip class which is referencing the child once it is added to the Stage:
package com.gameEngine.assetHolders
{
import com.gameEngine.documentClass.*;
import com.gameEngine.assetHolders.*;
import com.gameEngine.assetHolders.Levels.*;
import flash.display.*;
import flash.events.*;
public class FallingPlatform extends MovieClip
{
public var _document:Document;
// Trying to reference "_player"
public var _player:Player;
public var fallState:Boolean;
public var platformYSpeed:Number = 0;
public var platformGravityPower:Number = 0.75;
public function FallingPlatform()
{
this.addEventListener(Event.ADDED_TO_STAGE, initFallingPlatform);
// constructor code
}
public function initFallingPlatform(event:Event)
{
this.addEventListener(Event.ENTER_FRAME, dynamicFall);
this.addEventListener(Event.ENTER_FRAME, hitTest);
}
public function dynamicFall(event:Event)
{
if (this.fallState)
{
this.platformYSpeed += this.platformGravityPower;
y += this.platformYSpeed;
}
}
// Trying to reference "_player"
public function hitTest(event:Event)
{
if (this.hitTestPoint(_player.x, _player.y + 1, true))
{
this.fallState = true;
}
}
}
}
The player is initialized in the Document class, right? So for me, the best option is either passing the player reference in the constructor of your FallingPlatform class like this
public function FallingPlatform (thePlayer:Player) {
this._player = thePlayer
}
or having a setter method to pass it to it. In this way, you're not tying the structure of your code
public function set player (thePlayer:Player):void {
this._player = thePlayer
}
Hope it helps!
If you set a document class for a fla file every movieclip on the stage can be accessed by it's instance name - just as you wold create a var with its name.
Event more, you can do something like that:
If you place two movieclips on the stagefor example mc1 and mc2 you can add them as variables to the document class.
package{
public class DocClass{
public var mc1:MovieClip;
public var mc2:MovieClip;
[...]
}
}
and than you can access those movieclips from your class with code hints form your IDE (flash or flashbuilder)
the opposite is also availible: define variables in your class and than access them in flash
! it works best when your document class extends a Sprite, I haven;t tested it on extending from a MovieClip but it should also work

Controlling objects of another class that are already on the stage

I'm quite ashemed to ask this question here because I'm sure that I'm missing something very basic. I'm not even sure what should be the correct title for this question.
Let's say that I've a button object (instance of Flip) and a coin object (instance of Coin) on the stage. The coin object has two frames: one showing Heads and one for Tails.
MyCoin class is as following:
package
{
import flash.display.MovieClip;
public class Coin extends MovieClip
{
protected var _coinFace:uint;
public function Coin()
{
stop();
}
public function get coinFace():uint {
return _coinFace;
}
public function set coinFace(value:uint):void {
_coinFace = value;
}
public function show():void {
gotoAndStop(_coinFace);
}
}
}
Objective: When user clicks the button, the coin should flip and show a random coinFace. I've added an eventListener to the Flip class as follows:
public function Flip()
{
this.addEventListener(MouseEvent.CLICK, onMouseClick);
}
Problem: How do I reach the coin object on the screen via onMouseClick function? Let's say that the object on the stage has instance name of myCoin. I suppose that had I not done this with an external class and simply used actions from the frame I could just use the instance name as a variable. I couldn't figure to do the same it in an external class. Do I first create the object which is already on the stage?
Where you create the instance of each, the flip object needs to be passed an instance of the coin object.
var myCoin:Coin = new Coin();
var myFlip:Flip = new Flip(myCoin);
Then inside the Flip class:
private var _coin:Coin;
public function Flip(coin:Coin) {
_coin = coin;
this.addEventListener(MouseEvent.CLICK, onMouseClick);
}
private function onMouseClick(e:MouseEvent):void {
_coin.gotoAndStop(2); // Or what ever needs to be done to the coin on click
}
Alternatively, depending on the complexity of the overall structure, you can a build a control class that acts as a link between the two.

RemoveChild and Error 2025

I'd like to remove a child (background) via another class. I can't seem to be able to target it! It always returns me null or error 2025 and stuff... hehe.
I have the background in my class creationObjets:
package cem{
import flash.display.Sprite;
public class creationBackground extends Sprite{
public function creationBackground() {
switch(monterJeu._Difficulte){
case 0:
backgroundFacile();
break;
case 1:
backgroundMoyen();
break;
case 2:
backgroundDifficile();
break;
}
}
private function backgroundFacile():void{
var backgroundStage:Sprite = new Sprite();
backgroundStage.graphics.beginFill(0x8FCCA8);
backgroundStage.graphics.moveTo(0,0);
backgroundStage.graphics.lineTo(750,0);
backgroundStage.graphics.lineTo(750,450);
backgroundStage.graphics.lineTo(0,450);
backgroundStage.graphics.lineTo(0,0);
backgroundStage.graphics.endFill();
this.addChild(backgroundStage);
}
private function backgroundMoyen():void{
var backgroundStage:Sprite = new Sprite();
backgroundStage.graphics.beginFill(0x8F3378);
backgroundStage.graphics.moveTo(0,0);
backgroundStage.graphics.lineTo(750,0);
backgroundStage.graphics.lineTo(750,450);
backgroundStage.graphics.lineTo(0,450);
backgroundStage.graphics.lineTo(0,0);
backgroundStage.graphics.endFill();
this.addChild(backgroundStage);
}
private function backgroundDifficile():void{
var backgroundStage:Sprite = new Sprite();
backgroundStage.graphics.beginFill(0x233378);
backgroundStage.graphics.moveTo(0,0);
backgroundStage.graphics.lineTo(750,0);
backgroundStage.graphics.lineTo(750,450);
backgroundStage.graphics.lineTo(0,450);
backgroundStage.graphics.lineTo(0,0);
backgroundStage.graphics.endFill();
this.addChild(backgroundStage);
}
}
}
public static var _creationBackground:creationBackground = new creationBackground();
below, I add it:
addChild(_creationBackground);
then I want to remove it from another class actionObjets! How can I get to my background?
I tried
creationObjets._creationBackground.parent.removeChild(creationObjets._creationBackground);
removeChild(creationObjets._creationBackground);
I really have no idea how to access it!
I'm not sure if I understand your problem correctly but:
Remember that in order to remove a child you need access to the child's stage. If your actionObjects class is a Movieclip or sprite it will have a read-only variable that will reference the stage (which may or may not be the same as the stage you added _creationBackground too).
So for instance:
stage.removeChild(_creationBackground);
Should work fine if actionObjets has the same stage as wherever you added _creationBackground.
If actionObjets does not have the same stage or has none at all (maybe it isn't a sprite or movieclip?) You can pass in the stage where _creationBackground was added.
IE:
package {
import flash.display.Stage;
public class actionObjets {
private var myStage:Stage;
public function actionObjets(s:Stage) {
myStage = s;
}
}
}
and then try:
myStage.removeChild(_creationBackground);
This is of course assuming that you have access to the _creationBackground clip inside actionObjets.
Not sure if this addressed your problem or not,
good luck.
Any of the following should suffice:
creationObjets.removeChild(creationObjet.getChildAt(0));
or
creationObjets.removeChild(creationObjet.getChildByName("creationBackground"));
or
creationObjets.removeChildAt(0);
When you use removeChildAt() or getChildAt() you must specify the display object's(that you want to get or remove) index position. The index position is the position of the display object in the display list of the display object container(I've made the assumption that its 0).
Also when using getChildByName() you must specify the name of the display object that you want to get. Note that you must set the name property of the display object first.
Here's a working example based on you flash app/movie:
package
{
import cem.CreationObjet;
import cem.ActionObjet;
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main():void
{
init();
}// end function
private function init():void
{
var creationObjet:CreationObjet = new CreationObjet();
addChild(creationObjet);
var actionObjet:ActionObjet = new ActionObjet(creationObjet);
}// end function
}// end class
}// end package
In the document class Main, first CreationObjet and ActionObjet is imported. Next an instance of the CreationObjet is declared, instantiated and added to the stage. Lastly an instance of the ActionObjet is declared, instantiated and the instance of CreationObjet is parsed as its only argument.
package cem
{
import cem.CreationBackground;
import flash.display.Sprite;
public class CreationObjet extends Sprite
{
private var _creationBackground:CreationBackground;
public function CreationObjet():void
{
_creationBackground = new CreationBackground();
addChild(_creationBackground);
}// end function
}// end class
}// end package
In the CreationObjet class, an instance of CreationBackground is added to the CreationObjet display object.
package cem
{
import cem.CreationObjet;
public class ActionObjet
{
private var _creationObjet:CreationObjet;
public function ActionObjet(p_creationObjet:CreationObjet):void
{
_creationObjet = p_creationObjet;
_creationObjet.removeChild(_creationObjet.getChildAt(0));
// or _creationObjet.removeChild(_creationObjet.getChildByName("creationBackground"));
// or _creationObjet.removeChildAt(0);
}// end function
}// end class
}// end package
Finally in ActionObjet class, the CreationBackground display object is removed from the CreationObjet.
I've had to make a bunch of assumptions about your flash app/movie, but this should give you a general idea of how to implement what I suggested before.
I hoped this helped :)