Passing a variable between 2 actionscript3 files (.as) - actionscript-3

How can I pass a variable between 2 actionscript3 files (.as) ?
I have a main fla file and 3 as files, with a class each one...
I have a basic knownelge in AS2 but not too much in AS3
Thanks for help.

It seems that you are trying to access some variables from anywhere in Flash. One way is to create some class with static methods and variables. For example:
package somenamespace {
class Registry {
static private var something_:String;
public function get something():String {
return something_;
}
public function set something(v:String):void {
if (something_ === v) return;
something_ = v;
}
}
}
Then you can access this variable from anywhere in Flash:
Registry.something = "example";

The correct way to pass data between classes is to pass it by reference. This is better than maintaining data with global scope as it permits you to decouple your classes.
It's hard to give you specific advice without seeing your classes and the data you are trying to pass, but here's a simple example using a document class (Main.as) and a child class (Child.as):
Main.as:
package {
class Main extends MovieClip
{
private var someData:String;
//constructor
function Main()
{
//create the data
someData = "my string";
//create an instance of child
var child:Child = new Child(someData);
}
}
}
Child.as:
package {
class Child
{
private var someData:String;
//constructor
function Child(initData:String)
{
someData = initData;
trace(someData); // my string
}
}
}
It sounds like in your case you need a controller class to instantiate the classes you have and to manage the relationships between them. This would be a variation of the Main class above, instantiating all three classes and extracting and passing around their data.

Related

Accessing a variable defined by a constructor function through another class

I have two classes, the first one is called CoreModus. In CoreModus (which is a non-document class) I declare a "global" variable, called modus, using the constructor function CoreModus(modus);
CoreModus.as:
package myStudio.basic {
public class CoreModus {
public var modus:String;
public function CoreModus(structure:String) {
modus = structure;
}
public function setup():String {
return modus;
}
}
}
The second class is called Animation, which is a non-document class, and I want to access the variable modus, which is declared in CoreModus constructor function: i.e CoreModus("non-linear"); which in this case modus = non-linear.
Animation.as:
package myStudio.basic {
import fl.transitions.Tween
public class Animation {
public var anim:Tween;
public function Animation() {}
public function tryToRetrieveModus():void {
var modo:CoreModus = new CoreModus();
var modus:String = modo.getModus();
trace("I'm trying to retrieve the modus " + modus);
}
}
}
Of course, because CoreModus needs a parameter, I can't use the option I tried in Animation.as (making an instance of CoreModus).
FLA document, frame 1:
import myStudio.basic.CoreModus;
import myStudio.basic.Animation;
var modo:CoreModus = new CoreModus("non-linear");
var mov1:Animation = new Animation();
trace(modo.setup());
mov1.tryToRetrieveModus();
Is there any other way to access to this variable?
P.S. I omitted a bunch of unrelated lines in CoreModus(); constructor function. I don't want that code to be processed every time, for CPU's sake.
To access the variable from your CoreModus class instance, first create the instance of the class somewhere.
Then, pass the instance reference to the the Animation class instance. After that you can use the public variable of CoreModus as you please.
Here's an example:
//creating the CoreModus class instance
var myCoreModus:CoreModus = new CoreModus("my string");
//creating the Animation class instance
var myAni:Animation = new Animation();
myAni.modo = myCoreModus; // make sure that modo is public instance variable
You can also make your CoreModus a singleton and have a static variable if you have only one instance of your CoreModus.

Flex Strongly Typed Proxy Classes for Lazy Instantiation

Does anyone know of a framework, preferably some way to have the Flex compiler run an extension or perhaps just a build step that we could generate strongly typed proxy classes of our application's data models.
There are 2 main things we want to do with the proxy's:
At runtime we want to lazily parse and instantiate the instance as accessed (similiar to how Java's Hibernate has Lazy proxy objects)
In an editor application we want to implement setter calls so we can track which objects have been modified
The Proxy is really necessary in this situation beyond things like programatically setting up ChangeWatcther's because we need to track Array adds/remove and possibly track "reference" objects so that when a "reference key" is changed we know to save those objects that are referencing it by key
In the first case we want the proxy to basically abstract when that object is loaded from serialized data, but still pass around references of it with the same public properties and data access pattern if it were the real object.
Basically the proxy would instantiate the object the first time a method is called on it.
I know we could use some AS3 byte-code libraries like as3-commons-bytecode.
Or possibly repurposing the GraniteDS Code Generation.
I'd prefer to generate code because it is a deterministic thing and it'd be nice if we could have a way to debug it at runtime easier.
Does anyone know if I could do something like MXMLC does when it generates AS3 code from MXML files.
Also is there anyway to control "when" in the compilation pipeline I can generate code, because we have a lot of data objects using public fields instead of getter/setters, but that are [Bindable] and so if I could generate the proxy based on the generated getter/setter methods that would work.
Here's an example application data object and proxy classes:
[Bindable]
public class PersonDTO implements Serializable {
private var _name:String;
private var _age:Number
public function get age():Number {
return _age;
}
public function set age(a:Number):void {
_age = a;
}
public function get name():String {
return _name;
}
public function set name(n:String):void {
_name = n;
}
public void readObject(data:*) {
//...
}
}
// GENERATED CLASS BASED ON PersonDTO
public class LazyProxy_PersonDTO extends PersonDTO {
private var _instance:PersonDTO = null;
private var _instanceData:*;
private function getInstance():void {
if (_instance == null) {
_instance = new PersonDTO();
_instance.readObject(_instanceData);
}
}
override public function get age():Number {
//Ensure object is instantiated
return getInstance().age;
}
override public function get name():String {
//Ensure object is instantiated
return getInstance().name;
}
}
// GENERATED CLASS BASED ON PersonDTO
public class LogChangeProxy_PersonDTO extends PersonDTO {
//This will be set in the application
public var instance:PersonDTO;
//set by application
public var dirtyWatcher:DirtyWatcherManager;
override public function set age(a:Number):void {
dirtyWatcher.markAsDirty(instance);
instance.age = a;
}
}
Digging a little deeper into AS3-Commons byte code library it looks like they support generating proxy classes and interceptors.
http://www.as3commons.org/as3-commons-bytecode/proxy.html
public class DirtyUpdateInterceptor implements IInterceptor {
public function DirtyUpdateInterceptor() {
super();
}
public function intercept(invocation:IMethodInvocation):void {
if (invocation.kind === MethodInvocationKind.SETTER) {
if (invocation.arguments[0] != invocation.instance[invocation.targetMember]) {
invocation.instance.isDirty = true;
}
}
}
}

Re-defining named functions at runtime

What I am trying to do is kind of odd, but I am wondering if anyone can come up with a clever way to do what I want to do. Basically, I want to re-define a named function at runtime. I can do this with anonymous functions, but I can't figure out a way to do it for named functions. I want to do this so that I can implement a "spy" functionality on an object for a testing framework (a port of Jasmine to Flex).
Take, for instance, this class:
public class TestClass
{
public var anonymous:Function = function():void {
trace("original anonymous");
};
public function named():void {
trace("original named");
}
}
I can easily re-define the anonymous function because it is just a variable. Javascript uses this idiom a lot.
var testClass:TestClass = new TestClass();
testClass.anonymous = function():void { trace("overridden anonymous"); }
BUT, when I do the same thing for named functions, you get a compile-time error:
// Does not compile
testClass.named = function():void { trace("overridden named"); }
I tried to make it a bit more "squishy" but this leads to a runtime failure "Cannot assign to a method named on TestClass".
// Compiles with runtime failure
testClass["named"] = function():void { trace("overridden named"); }
Can anyone more clever than I come up with a way to hack this? Can the bytecode be hijacked? Something?
I want to modify an object, not a
class
But object doesn't contain functions, only non-static variables. I tried to use prototype property and replace method there, but original method still gets called instead of injected one.
About "hack" bytecode, do you mean "hack" already loaded SWF in runtime? I think it's not possible. I'm sure, though, you can parse SWF with something like as3swf, find method in bytecode, replace it and save result in new SWF.
I had an idea bout making a function "cache" . This might work with what you need.
Let's say you have a class "Car" with a method you need to redefine at runtime:
public class Car extends Sprite
{
private var functionCache:Function;
public function Car()
{
super();
}
public function flexibleFunction(functionBody:*=null):void{
if(functionBody is Function){
functionBody.call();
functionCache=functionBody;
} else {
functionCache(functionBody);
}
}
}
Usage:
public class Main extends Sprite
{
private var car:Car;
public function Main()
{
car = new Car();
car.flexibleFunction(function(){trace("redefine test #1")});
car.flexibleFunction();
car.flexibleFunction(function(doParametersWork:String="let's see"){trace("redefine test #2: " + doParametersWork);});
car.flexibleFunction("yes they do");
car.flexibleFunction();
}
}
an easy way to accomplish what you want is to simply pass a new function to the original function and execute it from there:
package
{
//Imports
import flash.display.Sprite;
//Class
public class RedefineFunction extends Sprite
{
//Constructor
public function RedefineFunction()
{
originalFunction();
originalFunction(redefinedFunction);
}
//Original Function
public function originalFunction(redefinition:Function = null):void
{
if (redefinition != null)
redefinition();
else
trace("Original Function Definition");
}
//Redefined Function
private function redefinedFunction():void
{
trace("Redefined Function Definition")
}
}
}
traces:
Original Function Definition
Redefined Function Definition

How do I distinguish between two imports with the same name in as3?

I have two ArrayUtil classes I need to access from the same class - they each come from external libraries and have different code, so I can't just combine them.
How do I disambiguate them in the code?
I tried doing com.adobe.utils.ArrayUtil inline, and leaving the existing import, but that resulted in an undefined property com error.
Cheers
// In main class/timeline etc
import package1.*;
import package2.*;
var a1:package1.A = new package1.A();
var a2:package2.A = new package2.A();
Package 1 Class
// In package1.A.as
package package1 {
public class A {
public function A() {
// constructor code
trace("P1");
}
}
}
Package 2 Class
// In package2.A.as
package package2 {
public class A {
public function A() {
// constructor code
trace("P2");
}
}
}
Output
// Outputs
P1
P2
If you need to access static methods or properties. You would do something similar to the following:
import com.adobe.utils.ArrayUtil;
var a:Array = ["A", "B", "C"];
trace(com.adobe.utils.ArrayUtil.arrayContainsValue(a, "B")); // outputs true
What are the names of the libraries? Try just mx.adobe.utils.ArrayUtil for the reference to the first class.
You can most likely use the package names to reference them directly (i.e. com.domandtom.MyUtils (open it up, and you'll see it defined at the top of the class file).

how do I make non-document-class classes 'aware' of stage components in Flash AS3?

I am working on a text adventure game which will have at least a few components (a text area for narrative and text input for user input) on the stage at all times. Therefore, I have created those components statically through Flash's WYSIWYG design environment. I gave them instance names "myTA" and "myTI" respectively. I was able to get my main class (the document class for the stage) to interact with them (dynamically adding text one character at a time like a typewriter at runtime), but other classes in the same package don't seem able to recognize the stage components. Below is the relevant code:
Case A, in which everything happens within the Main class:
package {
public class Main extends MovieClip {
public var myTA:TextArea;
var displayedChar:String = new String();
var textToWrite:String = new String();
var i:int = 0; var intervalId:uint;
var done:int = 0;
public function Main {
setUpTA();
}
public function setUpTA(){
myTA.text = "" + playAtInterval("Hello Player!");
}
public function writeCharsSlowly(){
textToWrite = arguments[0];
displayedChar=textToWrite.substring(i,i+1);
myTA.appendText(displayedChar);
i++;
if (i == textToWrite.length) {
done = 1;
clearInterval(intervalId);
}
}
public function playAtInterval(theText:String) {
i = 0;
intervalId = setInterval(writeCharsSlowly, 100, theText);
}
}
}
Case B, where Main calls on a second class 'TypeWriter' to handle the typewriter-printing:
Main:
package {
public class Main extends MovieClip {
public var myTA:TextArea;
public var myTI:TextInput;
var str:String = new String();
public function Main{
testTypeWriter();
}
public function testTypeWriter(){
typeW.playAtInterval("Hello Player");
typeW.addEventListener(MouseEvent.CLICK,testTypeWriter2);
typeW.addEventListener(KeyboardEvent.KEY_DOWN,inputEngine2)
addChild(typeW);
}
public function testTypeWriter2(event:MouseEvent){
if (myTI.text == "a") {
typeW.playAtInterval("yo");
} else {
typeW.playAtInterval("Greetings, I am a test...");
}
addChild(typeW);
}
public function inputEngine2(event:KeyboardEvent){
str = String.fromCharCode(event.charCode);
myTI.appendText(str);
}
TypeWriter:
package {
public class TypeWriter extends MovieClip {
public var myTI:TextInput;
public var myTA:TextArea;
var i:int = 0;
var done:int = 0;
var intervalId:uint;
var displayedChar:String = new String();
var textToWrite:String = new String();
public function TypeWriter(){
///nothing here
}
public function writeCharsSlowly(){
textToWrite = arguments[0];
displayedChar = textToWrite.substring(i,i+1);
myTA.appendText(displayedChar);
i++;
if (i == textToWrite.length) {
done = 1;
clearInterval(intervalId);
}
}
public function playAtInterval(theText:String) {
i = 0;
intervalId = setInterval(writeCharsSlowly, 100, theText);
}
}
}
Case A works, but in case B Flash is giving me the error "Error #1009: Cannot access a property or method of a null object reference" and notes the first line in TypeWriter where I try to operate on myTA as the problem.
how can I make other classes besides the document class 'aware' of existing stage components?
Thanks,
CCJ
I would recommend the Service Locator Pattern for this. The most naive approach would be to create a resource class which contains public static variables. Then in your document class you assign the stage instances to the corresponding static variable in the resource class. Then you can simply access these stage components anywhere.
var someTextArea = Resource.TA; //probably should rename to something more meaningful
For something a little more ingenious you should read the article I linked to.
I think this is better than the dependency injection as constructor injection could lead to huge parameter list as you might add more items to the stage, and I am not so fond on setter injection as it is easy to forget to set them.
EDIT:
Just to make it a bit more clear I thought I would add some code :)
Resource class
package
{
//TODO imports
public class Resource
{
public static var TA:TextArea;
public static var TI:TextInput;
}
}
Document class
//....setup function
Resource.TA = myTA; //myTA is the name of the instance on stage
Resource.TI = myTI;
Foo class
Resource.TA.x = 100;
//or
_myClassMemberVariable = Resource.TA;
_myClassMemberVariable.x = 100;
I think some dependency injection will solve this problem. When you instantiate your Typewriter class, pass references to myTA and myTI. ex:
public function Main{
testTypeWriter(this.myTA, this.myTI);
}
Then your Typewriter constructor should look like this:
public function TypeWriter(ta:TextArea, ti:TextArea){
this.myTA = ta;
this.myTI = ti;
}
This also has the benefit of making your application less tightly coupled, so for example you can reuse your Typewriter class with a different text area and text input.
Edit
Some extra info that may help you in the future: you can access stage elements through the root object. But this only works with objects that have been added to the display list. Let's say that Typewriter represents an object in the display list, you could then access myTA like this:
MovieClip(root).myTA
(Change MovieClip to Sprite if that's what your document class extends).
However, since it seems that Typewriter does not get added to the display list, I recommend using my first suggestion of dependancy injection.
Also check out this page, it dicusses using CasaLib to access the stage from any object. I personally haven't tried it, so that's why it's at the end here ;-)
Are myTA and myTI actually on the stage at author time or are the added dynamically?
In the first case, just add an instance name to each. Then add a variable to each class
var main_mc : Main = root as Main;
You can then access the instances via main_mc.myTA and main_mc.myTI (assuming those are the instances names you chose) and everything should be type-safe.
In the dynamic case, just make sure you save off references in the main class to each as you add them.
Another option is to have classes for myTA and myTI send an event in their constructor to announce their existence. The main class can then listen for these and register the references.
To be honest, though, you are mixing up your display with your logic. Read up on MVC and PureMVC in particular. With a good design, everything can be handled by messages, and the instances don't need direct references to each other. IIRC, Moock's AS3 book has a chapter on MVC (but it could be his AS2 book).
The fact that they will be on stage at all times shouldn't stop you from creating specific classes for them.
Depending on your game structure, you could either create a MovieClip with your TextFields and link them to the Typewriter class, or simply create a class for the TextFields and use Events to modify the text content.
You're using external classes so there are no reason to keep any kind of logic inside Flash CS.
Just to be clear. When you are writing stage you really mean the document class for your flash document. Stage is a class that every instance that has been added to the displaylist Have access to.
I would pass the textfields into the classes that needs to update them.
var tw : Typewriter = new Typewriter();
tw.inputField = myTI;
tw.textArea = myTA;
Or
var tw : Typewriter = new Typewriter(myTI, myTA);