Read a variable from a different Class in Actionscript 3 - actionscript-3

I have already set a variable in my document class "Main.as". I am now trying to access that variable and read its value from a different Class and Function, take that value and email it.
For example in my "Main.as" file I have this function:
public var _myVar:String;
function create() {
_myVar = "hello";
}
Now from my other class "EmailtoFriend.as" I have a new function to try and get the value of that pre set variable:
function getVar() {
trace(_myVar);
}
Why will it not output "hello"? Instead I get an error saying: Access of undefined property _myVar. If I could just get this simple example working, I think it will help me understand a lot of things. Thanks!

The error your getting really says it all. Although _myVar is defined in your Main class public var _myVar:String;, it isn't defined in your Emailtofriend class. If you want access to _myVar you need to do one of the following:
Parse a reference of your Main object(using this) to your EmailToFriend class:
Main.as(document class):
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public var _myVar:String;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
public function create():void
{
_myVar = "hello";
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
create();
var emailToFriend:EmailToFriend = new EmailToFriend(this);
emailToFriend.getVar();
}// end function
}// end class
}// end package
internal class EmailToFriend
{
private var _main:Main;
public function EmailToFriend(main:Main)
{
_main = main;
}// end function
public function getVar():void
{
trace(_main._myVar);
}// end function
}// end class
Or to make _myVar a public static property of Main and access it via Main._myVar:
Main.as(document class):
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public static var _myVar:String;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
public function create():void
{
_myVar = "hello";
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
create();
var emailToFriend:EmailToFriend = new EmailToFriend();
emailToFriend.getVar();
}// end function
}// end class
}// end package
internal class EmailToFriend
{
public function EmailToFriend() {}
public function getVar():void
{
trace(Main._myVar);
}// end function
}// end class
Also one small thing, when using underscores for class properties, you should only use them for private properties, not public. Well I say only but I really mean it's more common.
[UPDATE]
This is in response to your comment:
Main.as:
package
{
import EmailToFriend;
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public static var _myVar:String;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
public function create():void
{
_myVar = "hello";
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
create();
var emailToFriend:EmailToFriend = new EmailToFriend();
emailToFriend.getVar();
}// end function
}// end class
}// end package
EmailToFriend.as:
package
{
import Main;
public class EmailToFriend
{
public function EmailToFriend() {}
public function getVar():void
{
trace(Main._myVar);
}// end function
}// end class
}// end package

All variables implicitly have a target, unless explicitly specified. Variables without an explicit target will commonly look in the local scope of the function (in this case, getVar()) and the global scope of the class (in this case, EmailToFriend).
I assume that these don't exist in your code, judging by the error. You will need something like the following to access the var:
function getVar() {
var main:Main = new Main();
main.create();
trace(main._myVar);
}

Assuming Main.as is your document class:
public var _myVar:String;
public function create():String {
//we specify that this function will return a String using the :String in the above line
//now give the variable a value
_myVar = "hello";
//and send it back to the caller of the function
return _myVar;
}
Within your other class
function getVar():void {
//MovieClip(root) is another way of referencing your document class.
trace(MovieClip(root).create());
}
OR...
public var _myVar:String;
public function create():void {
//now give the variable a value
_myVar = "hello";
}
Within your other class
function getVar():void {
//call the function that actually gives myVar a value
MovieClip(root).create();
//now you can trace the value
trace(MovieClip(root)._myVar);
}

package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public var _myVar:String;
public function Main(){
create();
}
private function create() {
_myVar = "hello";
}
}
}
}
in EmailtoFriend.a
import Main.as
var myMain = new Main();
trace(myMain._myVar);

What you should do is have a OOP approach, meaning use encapsulation in your classes. If you don;t know what that means, its ok. For instance, if you have a variable, that you want to be accessible, then you should really make it private, and the set up its own public function that returns the variable. Like this:
package {
public class SomeClass {
private var someVar:Number = 12; // A private variable, which means only this class can
// use the reference someVar, and only other outiside classes can use the function getSomeVar.
... // skip some regular class stuff
public function getSomeVar():Number {
return this.someVar; //returns the variable someVar from this class to whoever is accessing it.
//This function is public which means that anyone can call it and get the variable someVar.
}
}
}
To access that variable, you just reference a class instance:
var someClass:SomeClass = new SomeClass(); // create the instance using the variable someClass
var myVar:Number = someClass.getSomeVar(); // ACCESSES the variable that you want from the class,
//by first using the class instance reference, and then calling its public function that returns the value you want.

Related

Actionscrpipt 3 OOP

I am developing player which has several my own custom developed buttons which has their own classes. Also the player has its own class which is the main class and instansiate all the buttons I have.
So this is a simple example of "has a" relationship i.e composition.
I used to past a reference of the player trought every buttons constructor in order buttons to be able to access properties and methods from the players class. This method is working good, but the problem is it has a lot of duplicate code than needs to be added to every button class.
So I tried to workit out by using inheritance, i.e buttons extend the playes main class.
But this way although i declare all properties protected I get the final swf blank white. So there must a problem.
Am I doing the structure wrong or what? Any idea?
Here is a sample code
public class Player extends MovieClip{
protected var prop1:Number;
protected var prop2:String;
protected var playButton:PlayButton;
....
public function Player(){
// with composition
playButton=new PlayBUtton(this);
// with inhgeritance
playButton=new PlayButton();
addChild(playBUtton);
}
}
//first implementation with composition
public class PlayButton extends MovieCLip{
public function PlayButton(player:Player){
//access Player trough player parameter
}
}
//second implementation with inheritance
public class PlayButton extends Player{
public function PlayButton(){
//access Player trough inheritance
}
}
You could use the state design pattern like in the following example I made:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
var player:Player = new Player();
addChild(player);
}// end function
}// end class
}// end package
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
internal class TextButton extends Sprite
{
public function TextButton(text:String)
{
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, 100, 25);
graphics.endFill();
var textField:TextField = new TextField();
textField.text = text;
textField.mouseEnabled = false;
textField.x = this.width / 2 - textField.textWidth /2;
textField.y = this.height / 2 - textField.textHeight / 2;
addChild(textField);
}// end function
}// end class
internal interface IState
{
function play():void
function stop():void
}// end interface
internal class Player extends Sprite
{
private var _playState:IState;
private var _stopState:IState;
private var _state:IState;
private var _playButton:TextButton;
private var _stopButton:TextButton;
public function get playState():IState { return _playState }
public function get stopState():IState { return _stopState }
public function get state():IState { return _state }
public function set state(state:IState):void { _state = state }
public function Player()
{
_playState = new PlayState(this);
_stopState = new StopState(this);
_state = stopState;
_playButton = new TextButton("PLAY");
_playButton.addEventListener(MouseEvent.CLICK, onClick);
addChild(_playButton);
_stopButton = new TextButton("STOP");
_stopButton.addEventListener(MouseEvent.CLICK, onClick);
_stopButton.x = 110;
addChild(_stopButton);
}// end function
private function onClick(e:MouseEvent):void
{
var textButton:TextButton = e.target as TextButton;
switch(textButton)
{
case _playButton : _state.play(); break;
case _stopButton : _state.stop(); break;
}// end function
}// end function
}// end class
internal class PlayState implements IState
{
private var _player:Player;
public function PlayState(player:Player)
{
_player = player;
}// end function
public function play():void
{
trace("player already playing");
}// end class
public function stop():void
{
_player.state = _player.stopState;
trace("stopping player");
}// end function
}// end class
internal class StopState implements IState
{
private var _player:Player;
public function StopState(player:Player)
{
_player = player;
}// end function
public function play():void
{
_player.state = _player.playState;
trace("playing player");
}// end function
public function stop():void
{
trace("player already stopped");
}// end function
}// end class

AS3 - Get button instance name and path

How to trace that "section1.btnback" was clicked?
section1.btnback.addEventListener(MouseEvent.CLICK, getBack)
function getBack(event:MouseEvent):void
{
// trace: "section1.btnback"
}
Thanks.
Uli
I'm not sure what actualy you are asking for. If my suggestion is not the right for you, please excuse me. If you write
trace(event.target)
you will see the complete name of the instance of the button.
I am guessing that you want it to say something like:
"Button was clicked!" to console each time the button is pressed?
trace("Section1.btnback was clicked!");
I believe.
(Wasn't really sure what you were asking for)
-Expanded in comments below-
If you want the "complete path" like meddlingwithfire suggested in his comment on 3lionz's answer, then you can use a class I created that does it for you. The class is called DOPath(short for display object path). The following is an example of it being used to get the complete path of a display object:
Main.as(document class):
package
{
import com.example.display.DOPath;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
var container1:Sprite = new Sprite();
container1.name = "container1";
var container2:Sprite = new Sprite();
container2.name = "container2";
var square:Square = new Square();
square.name = "square";
square.addEventListener(MouseEvent.CLICK, onSquareClick);
addChild(container1);
container1.addChild(container2);
container2.addChild(square);
}// end function
private function onSquareClick(e:MouseEvent):void
{
var square:Square = e.target as Square;
var doPath:DOPath = new DOPath(square);
trace(doPath); // output: stage.root1.container1.container2.square
}// end function
}// end package
}// end package
import flash.display.Sprite;
internal class Square extends Sprite
{
public function Square()
{
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
}// end function
}// end class
DOPath.as:
package com.example.display
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Stage;
public class DOPath
{
private var _parents:Vector.<DisplayObjectContainer>;
private var _d:DisplayObject;
public function DOPath(d:DisplayObject):void
{
_d = d;
init();
}// end function
private function init():void
{
_parents = new Vector.<DisplayObjectContainer>;
pushParent(_d);
}// end function
private function pushParent(d:DisplayObject):void
{
if(d.parent)
{
_parents.push(d.parent);
pushParent(d.parent);
}// end if
}// end function
public function toString():String
{
var path:String = _d.name;
for (var i:uint = 0; i < _parents.length; i++)
{
var name:String = (_parents[i] is Stage) ? "stage" : _parents[i].name;
path = name + "." + path;
}// end for
return path;
}// end function
}// end class
}// end package
You can use a quite simple recursive function like this one:
private function getDOname(dob : DisplayObject) : String
{
var result : String = dob.name;
if (dob is UIComponent)
{
result = UIComponent(dob).id;
}
if(dob.parent && dob.parent is DisplayObjectContainer)
{
result = getDOname(dob.parent) + "." + result;
}
return result;
}
You can pass event.target to the function and acquire the button that was clicked. If you are using these techniques for debug purposes, you can simply add and event listener for click event to the stage or the SandboxRoot itself, if you are unsure which visual element you are actually clicking.

Get composed MovieClip's containing (parent) class after event

I have a MovieClip that is composed in a parent (non-display object) class. We register an event listener against that movieclip - a CLICK handler for example.
With event.target I can get a reference to the MovieClip from within the event handler. But how can I pull a reference to its composing class?
I could simply add a "parentClass" property on the dynamic MovieClip class, but I'm wondering if there's a more elegant/idiomatic way of doing it that I should consider?
If the class that creates your MovieClip is not a display object then it cannot really be considered its parent. The parent element will be that which your MovieClip is attached to. All that the creating class contains is a reference to an object, just the same as if you then refer to the MovieClip elsewhere.
My preferred way would be to create a descendant class of MovieClip that can contain a reference to the creating class, which you would then use instead of MovieClip.
package {
import flash.display.MovieClip;
public class MovieClipWithRef extends MovieClip
{
private var _parentRef:Object; //obviously cast this as accurately as possible
public function MovieClipWithRef($ref:Object):void
{
_parentRef = $ref;
}
public function get parentRef():Object
{
return _parentRef;
}
//no setter makes this property read-only once set by the constructor
}
}
This answer is essentially the same as shanethehat's answer but also incorporates the composite design pattern. Look at the following example:
ICompositeObject.as:
package com.flashdevelopprojects.patterns.composite
{
public interface ICompositeObject
{
function get parentObject():Object
}// end interface
}// end package
CompositeObject.as:
package com.flashdevelopprojects.patterns.composite
{
public class CompositeObject implements ICompositeObject
{
private var _parentObject:Object;
public function get parentObject():Object { return _parentObject }
public function CompositeObject(parentObject:Object)
{
_parentObject = parentObject;
}// end function
}// end class
}// end package
Main.as(document class):
package
{
import com.flashdevelopprojects.patterns.composite.CompositeObject;
import com.flashdevelopprojects.patterns.composite.ICompositeObject;
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
var a:A = new A();
a.b.addEventListener(Event.ADDED_TO_STAGE, onBAddedToStage);
addChild(a.b);
}// end function
public function onBAddedToStage(e:Event):void
{
var b:ICompositeObject = ICompositeObject(e.target);
trace(b.parentObject); // output: A
}// end function
}// end class
}// end package
import com.flashdevelopprojects.patterns.composite.CompositeObject;
import com.flashdevelopprojects.patterns.composite.ICompositeObject;
import flash.display.MovieClip;
internal class A
{
private var _b:B;
public function get b():B { return _b }
public function A()
{
_b = new B(this);
}// end function
public function toString():String { return "A" }
}// end class
internal class B extends MovieClip implements ICompositeObject
{
private var _compositeObject:CompositeObject;
public function get parentObject():Object { return _compositeObject.parentObject }
public function B(parentObject:Object)
{
_compositeObject = new CompositeObject(parentObject);
}// end function
}// end class

How to manage loaded assets?

I'm working on an AS3 project that must load a lot of external files; images, sound clips, movie clips etc. The program first loads an XML file that contains the file names of all assets needed by each class. From that point on I'm not sure what would be the best way to distribute those assets.
I thought about making a dedicated assets class that will load everything the XML object describes, and then pass it to each class' constructor so they can access the objects they need. This seems like the "tidiest" option, but is there a best practice way for managing loaded assets?
You could create a class that handles loading the assets and storing them as bytes. Then you can get an asset from that class via a reference(e.g. name) and do what you need with it. I've created an example to demonstrate this:
package
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.utils.ByteArray;
import flash.events.Event;
public class Main extends Sprite
{
private var _assets:Assets;
private var _counter:int;
private var _assetsXml:XML;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
_assetsXml = <assets>
<asset name="chrysanthemum" url="assets/chrysanthemum.jpg"></asset>
<asset name="kalimba" url="assets/kalimba.mp3"></asset>
<asset name="wildlife" url="assets/wildlife.wmv"></asset>
</assets>;
_assets = new Assets();
for each(var asset:XML in _assetsXml.children())
{
_assets.addEventListener(Event.COMPLETE, onAssetComplete);
_assets.load(asset.#name, asset.#url);
}// end for each
}// end function
private function onAssetComplete(e:Event):void
{
if (++_counter == _assetsXml.children().length()) addImage();
}// end function
private function addImage():void
{
var loader:Loader = new Loader()
loader.loadBytes(_assets.getAssetBytesByName("chrysanthemum"));
addChild(loader);
}// end function
}// end class
}// end package
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
internal class Assets extends EventDispatcher
{
private var _assetLoaders:Vector.<AssetLoader>;
public function Assets():void
{
_assetLoaders = new Vector.<AssetLoader>();
}// end class
public function load(name:String, url:String):void
{
var assetLoader:AssetLoader = new AssetLoader(name);
assetLoader.addEventListener(Event.COMPLETE, onAssetLoaderComplete);
assetLoader.load(url);
}// end function
public function getAssetBytesByName(name:String):ByteArray
{
for each(var assetLoader:AssetLoader in _assetLoaders)
{
if (assetLoader.name == name) break;
}// end for each
return assetLoader.bytes;
}// end function
private function onAssetLoaderComplete(e:Event):void
{
var assetLoader:AssetLoader = e.target as AssetLoader;
_assetLoaders.push(assetLoader);
dispatchEvent(new Event(Event.COMPLETE));
}// end function
}// end class
internal class AssetLoader extends EventDispatcher
{
private var _name:String;
private var _url:String;
private var _bytes:ByteArray;
public function get name():String { return _name }
public function get url():String { return _url };
public function get bytes():ByteArray { return _bytes };
public function AssetLoader(name:String):void
{
_name = name;
}// end function
public function load(url:String):void
{
_url = url;
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, onUrlLoaderComplete);
urlLoader.load(new URLRequest(_url));
}// end function
private function onUrlLoaderComplete(e:Event):void
{
var urlLoader:URLLoader = e.target as URLLoader;
_bytes = ByteArray(urlLoader.data);
dispatchEvent(new Event(Event.COMPLETE));
}// end function
}// end class

Get the stage dimensions without using a Singleton?

I have a few classes in my project which aren't display objects, but they need to know about the stage of my project (stage.stageWidth, stage.stageHeight).
Is there a simple way to pass this information along to my classes without using a Singleton or passing these items in as parameters into the constructor??
You can store a reference to stage in a class's static property which can be accessed from any class in your application. The following is an example of this:
GlobalVars.as:
package
{
import flash.display.Stage;
public class GlobalVars
{
public static var stage:Stage;
}// end class
}// end package
Main.as:
package
{
import flash.display.Sprite;
public class Main extends Sprite
{
private var _stageAccessor:StageAccessor;
public function Main():void
{
init();
}// end function
public function init():void
{
GlobalVars.stage = stage;
_stageAccessor = new StageAccessor();
_stageAccessor.traceStageWidth(); // output:
_stageAccessor.traceStageHeight(); // output:
}// end function
}// end class
}// end package
StageAccessor.as:
package
{
import flash.display.Stage;
public class StageAccessor
{
public function StageAccessor():void {};
public function traceStageWidth():void
{
trace(GlobalVars.stage.stageWidth);
}// end function
public function traceStageHeight():void
{
trace(GlobalVars.stage.stageHeight);
}// end function
}// end class
}// end package
There is no way to do it without a singleton if you don't have a reference to a displayed graphic object.