AS3 create a variable in root from within a function - actionscript-3

I have a fairly big swf right now with a bit of coding already. Most vars are created in the root, but now I have a problem.
I want to reload the flash swf (reset), and for that, I need to create a function that destroys all the vars and another one that creates them. At the moment, I have a javascript function that reloads the page, but that really isnt a good solution.
The problem is that when I create a var inside a function, it doesn't get created in "MovieClip(root)", and instead is only related to the function, thus rendering my swf unable to work.
Is there a way to create vars in MovieClip(root) from within a function? Or is there an alternative to what I'm trying to do?
EDIT: Added some example code.
function SetVar():void{
var test:String= new String("foobar");
}
SetVar();
trace(test);
...and the output is:
Scene 1, Layer 'Layer 1', Frame 1, Line 7 1120: Access of undefined property test.
Which is normal, because the "var test" is not global, so it was lost when the function ended. I want to make it so the function "SetVar()" adds the vars to the root, or global.

You need to read up on how scope works.
Basically:
An object declared within another object (be it a Class, Function, Object, or Loop), is only available within that specific object or loop iteration.
Object scope is inherited by children, not by parents. So a function within a class has access to an object declared within that class, but a class does not have access to an object declared within a function
A parent (or any other object) can access objects declared within child classes, but only if it is a public object
So looking at those basic rules (they are very, very basic. If you are just starting out, I urge you to do some proper research into object scope in OOP. It is the basis of everything you will do in dozens of languages), you are declaring an object in a function and trying to access it from outside that function. This breaks Rule #1 from above.
Instead, try this:
var test:String;
function setVar():void{
this.test = 'foorBar';
}
trace(test); //output: null (undeclared)
setVar();
trace(this.test); // output: fooBar
Looking at this, I did two things:
I moved the declaration of test into global space, meaning any object in that object will have access to it
I renamed SetVar to setVar. This has nothing to do with your question, but in AS3, the standard naming conventions dictate you use lowerCaseCamelCase for all objects (including functions), UpperCaseCamelCase for all Class names, and lowercasename for all package names. Again, unrelated but it is good to learn.
Now, ideally, you would probably want to do that setVar function slightly differently. To allow for better abstraction (basically making your code as generic an reusable as possible), you would want to return the value from the function rather than manually set the variable in the function.
var test:String;
var anotherTest:String;
function setVar():String {
return 'foorBar';
}
this.text = setVar();
this.anotherTest = setVar();
trace(this.test); // output: fooBar
trace(this.anotherTest); // output: fooBar
So that allows you to use that function with any String variable imaginable. Obviously, that is not very useful here since it doesn't do any logic. But I am sure you can see how that could be expanded with more code to make it more dynamic and much more useful
EDIT: As an afterthought, I used the this keyword. In AS3 (and a few other languages), this refers to the scope of the current class (or current frame, in case of timeline frame coding). So this.test refers to a variable test declared in the scope of the frame or class.

I am not entirely sure what you are looking for because there is no code associated with your question. However I will impart a bit of information I feel relates to the subject.
if you declare your variables in the class then you can reference them from a function as such:
package{
import flash.display.MovieClip;
public class DocumentClass extends MovieClip{
public var example:String = 'dog';
public function DocumentClass(){
trace(example); // dog
testFctn();
trace(example); // frog
}
public function testFctn(){
example = 'frog'
}
}
}
if you want to reference the variable of a parent class this.parent['variableName'] can be useful too. or a sibling of your working class sharing a parent class, this.parent['childClass']['variableName'] ...

Since you are declaring the variable within the function, its scope is restricted to that function only.
Try declaring the variable outside the function and initializing it in the function instead.
You should then be able to access it from root.
But if you wish to declare a variable on root from within a function (highly unusual requirement) then you can try doing:
document["variableName'] = value;
or
root["variableName'] = value;
inside the function.

Related

Error #2136: swf contains invalid data

public function starttank(event:MouseEvent):void
{
var Tankdrive:TankDrive = new TankDrive();
Tankdrive.tankstart();
}
It's saying that something in that function contains invalid data.
I have no idea what it is, i checked TankDrive and tankstart(); and both are correct, tankstart() is a public function...
I'm stumped...
EDIT: Error:
Error #2136: The SWF file file:///C|/Users/BigRed/Desktop/TankDrive/TankDrive.swf contains invalid data.
at mainmenu/starttank()
That's the error...
And above the starttank() function is still the same...
Ok, I looked over the files.
I'm puzzled by what you are trying to do.
Your Document class is TankDrive, and it creates an instance of mainmenu, which create ANOTHER instance of TankDrive when you click a button. Which is not what I think you want to do, or you think it's allowing you to access your document class. -- it's not. It's attempting to create a whole new instance of your game.
In short, your design is not making sense, and it's also causing a conflict with the document class resulting in that error.
The quick solution is to NOT have your EventListener & handler for the click in the mainmenu class, and then call the tankstart() method of your document class instead of attempting to create a new instance of TankDrive.
First move that listener into the document class and modify like this :
main.enterTank.addEventListener(MouseEvent.CLICK, starttank);
and then move your handler to the TankDrive class and modify as follows :
public function starttank(event:MouseEvent):void
{
tankstart();
}
This is not the only way to do this, and not really the way that I would do it. But I think that discussion is beyond the scope of this question.
If you want you can contact me at prototype.in.training#gmail.com for more details on that.
The problem is that you have an instance name that's also a class name (and the compiler has assumed that you meant to reference the latter). You probably meant to call your TankDrive variable 'tankDrive', so changing your code snippet to the following will probably help:
public function starttank(event:MouseEvent):void
{
var tankdrive:TankDrive = new TankDrive();
tankdrive.tankstart();
}
It's common and recommended to start all public variable names with a lower case letter, private variables with an underscore, and class names with a capital, though this isn't enforced by the language itself, so this kind of thing can happen.
Here are some guides on conventional variable and function naming that can help you keep track of what each of your vars and functions are supposed to do:
http://www.adobe.com/devnet/actionscript/learning/as3-fundamentals/variables.html
http://www.adobe.com/devnet/actionscript/learning/as3-fundamentals/functions.html
Good luck!

Compatability when passing object to class

Ok, so this might be me being pendantic but I need to know the best way to do something:
(This is psudocode, not actual code. Actual code is huge)
I basically have in my package a class that goes like this:
internal class charsys extends DisplayObject {
Bunch of Variables
a few functions
}
I another class which I intend to add to the timeline I want to create a function like this:
public class charlist {
var list:Array = new Array();
var clock:Timer = new Timer(6000);
var temp:charsys;
function addObj(MC:DisplayObject, otherprops:int) {
temp=MC;
temp.props = otherprops;
list.push(temp)
}
function moveabout(e: event) {
stuff to move the items in list
}
function charlist() {
stuff to initialize the timers and handle them.
}
}
So the question is, is my method of populating this array a valid method of doing it, is there an easier way, can they inherit like this and do I even need to pass the objects like I am?
(Still writing the package, don't know if it works at all)
Yes, you can pass an object into a function, but you should be careful of what you are planning to do with that object inside that function. Say, if you are planning to pass only charsys objects, you write the function header as such:
function addObj(MC:charsys, otherprops:int) {
Note, the type is directly put into the function header. This way Flash compiler will be able to do many things.
First, it will query the function body for whether it refers to valid properties of a passed instance. Say, your charsys object does not have a props property, but has a prop property, this typing error will be immediately caught and reported. Also if that props is, for example, an int, and you are trying to assign a String value to it, you will again be notified.
Second, wherever you use that function, Flash compiler will statically check if an instance of correct type charsys is passed into the function, so if there is no charsys or its subclass, a compilation error is thrown.
And third, this helps YOU to learn how to provide correct types for functions, and not rely on dynamic classes like MovieClip, which can have a property of nearly any name assigned to anything, and this property's existence is not checked at compile time, possibly introducing nasty bugs with NaNs appearing from nowhere, or some elements not being displayed, etc.
About common usage of such methods - they can indeed be used to create/manage a group of similar objects of one class, to the extent of altering every possible property of them based on their corresponding values. While default values for properties are occasionally needed, these functions can be used to slightly (or not so slightly) alter them based on extra information. For example, I have a function that generates a ready-to-place TextField object, complete with formatting and altered default settings (multiline=true etc), which is then aligned and placed as I need it to be. You cannot alter default values in the TextField class, so you can use such a function to tailor a new text field object to your needs.
Hope this helps.
This would work, I think I would assign values to the properties of the charsys object before passing it into the add method though, rather than passing the properties and having a different class do the property assignment. If you have some common properties they could either have defaults in charsys class definition or you could set literals in the addObj method.

How do I pass reference of an initialised object from one class to another class, to add (addChild) to display list?

I created this simple example
because I was using a more complex class, a menu item that I wanted to initialise all the settings in the Main class and then add it in in the Game class (and updating it) when needed (both classes are separate)
Class: Main (document class, is (ideally) where everything is initialised/created)
public class Main extends MovieClip
{
//testing passing on reference to Game
private var testBitmap:Bitmap;
private var testBitmapData:BitmapData;
private var testArray:Array;
public function Main():void
{
testBitmapData = new BitmapData(256, 256, false, 0xCCDDEE);
testBitmap = new Bitmap(testBitmapData);
testArray = [];
testArray.push(testBitmap); //Array for reference
game = new Game(540, 960, testArray);
//create Game class, pass reference to Object
game.x = 0;
game.y = 0;
}
}
Class: Game (created by document class, is (ideally) where everything runs)
public class Game extends MovieClip
{
private var testingArray:Array
public function Game(gameWidth:int, gameHeight:int, testArray:Array):void
{
this.testingArray = testArray; //assign to local Array and access
addChild(testingArray[0]);
//addChild to Game from an item intialised in Main, doesn't work >___<
}
}
.
.
.
the thing is, in my original Game class; it receives an initial bundle of cached BitmapData and a list Array that tells it which BitmapData it needs to cycle through
cut-down here (and that reference only for updating works (if I addedChild in Main already):
public function Game(gameWidth:int, gameHeight:int, cachedBitmapClipArray:Array)
{
this.cachedBitmapClipArray = cachedBitmapClipArray;
private function update():void
{
for each (tempCachedBitmapClip in cachedBitmapClipArray)
{
tempCachedBitmapClip.updateCurrentTile();
//but updating through the reference to an item initialised in Main works !!
}
}
}
.
how do I make the reference and passed in objects (or have access to) behave as in the original instance ?? (being able to addChild)
i.e. can objects cross 'scopes' (??) or should objects only be controlled (instantiated) in the class where they have been initialised
Well to answer the last question, yes objects can be passed from one object to another. I'm having a hard time understanding what exactly the problem is though. In generic programming terms Object or any other complex type are passed by reference, primitive types are also passed by reference but the internals of the AVM handle them in such a way as to treat them as passed by value. For a pretty clear (in my eyes at least) explanation of how arguments are passed via function/method calls, read here:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f56.html
Check out other areas in the tree navigation on the left for more details on the language itself.
One thing to note though I think the compiler will work around this error, in the first bit of code you posted Game has a return value of :void for a constructor there should be no declared return type since it should be implicitly typed as whatever the class is.
To add some explanation in my own words regarding pass by reference or pass by value. Pass by value means that the value of the object, that is the bytes stored at the location pointed to by the variable are actually copied to a new memory location which is then used. What this means in practice is they are not the same bytes of memory but rather they are two separate memory locations each with the same value stored in them, modification of one value after the passing doesn't affect the original. Pass by reference is to say you pass the memory location therefore no new memory for the value is allocated, both values are actually pointing to the same memory location (the pointer to them itself may be different but that pointers both point to the same location in memory). In this case the objects are the same.
What you're doing is advisable, dividing the labor and enapsulating particular types of functionality in classes does make the code easier to work with. I think the only problem would be if the Game object itself is never added as a child to something in the display tree (something that is attached to the stage). It appears you're creating a new Game and setting it's position but never adding it as a child to the stage, then adding anything to Game is never a part of the display tree.

AS3 - Parametrized Factory method using actual class name

Rather than use a hard-coded switch statement where you pass it the string name of a class and it then instantiates the appropriate class, I'd like to pass the actual name of the class to my factory method and have it dynamically create an instance of that class. I thought it would be trivial and am surprised it is not working. I must be missing something quite basic:
sample code:
createProduct(50, "Product1Class");
createProduct(5, "Product2Class");
private function createProduct(amount:uint, productClassName:String):void {
var productReference:Class;
try {
productReference = getDefinitionByName(productClassName) as Class;
for (var i:uint = 0; i < amount; i++) {
var product = new productReference() as ProductBaseClass; // throws reference error!
}
} catch (error:ReferenceError) {
throw new ReferenceError(error.message + " Have you linked a library item to this class?");
}
}
The only thing that may be a little odd (not sure) is that these "products" are actually linked Library items (ie: I have a movieClip in the Library that has a linkage to Product1Class and another to Product2Class both of which extend ProductBaseClass, which in turn extends MovieClip.
Why the ReferenceError?
If you have a runtime loaded library then the Class's are not compiled into the main swf, so you get the runtime reference error when you try to create them.
To work around this you can declare "dummy" vars of the classes you want to compile, or if using the flex compiler there are options to include the classes you are missing.
e.g. declare these anywhere in your project
private var p1:Product1Class;
private var p2:Product2Class;
Its a frustrating problem, if your classes extend MovieClip which is a dynamic class you might be able to access the properties etc by doing something like this:
var product:MovieClip = new productReference() as MovieClip;
p1["someCustomProperty"]; //Dot notation might work here as it is a dynamic class
Chris is absolutely right, the ReferenceError is actually being thrown during the call to getDefinitionByName, meaning that the reflection method cannot find Product1Class or Product2Class in your application domain. You can always check if a definition is available by checking the application domain directly, like:
// inside your createProduct method, yields 'false'.
ApplicationDomain.currentDomain.hasDefinition( productClassName );
Are these library assets loaded in at runtime? If so, you can either make sure that the library swf is loaded into the current application domain by adding an appropriately configured LoaderContext to your Loader, or you can replace the call to getDefinitionByName with the loaded swf's application domain's getDefinition method.
getDefinitionByName() and ApplicationDomain.currentDomain.hasDefinition() require full qualified class names. The example code in the original post works when Product1Class and Product2Class are in the default package. However, if you move the product classes to another package, you have to make sure that you are supplying the fully qualified class name to getDefinitionByName().
So if we put our product classes in com.example.products, then the call becomes:
productReference = getDefinitionByName("com.example.products.Product1Class") as Class;
I'm not really sure what the best practice is with this kind of dynamic factory class, but what I ended up doing (since all products were in the same package) was to create a constant within my factory class that defines the package for my products:
private const PRODUCT_PACKAGE:String = "com.example.products."; // note the trailing period
So that way your client code doesn't need to know (nor define) the product package. You just prepend this constant to your product class name when using getDefinitionByName().

trouble accessing non-static functions from static functions in AS3

I have a class containing, among other things, a drop down menu. With the aim of saving space, and since the contents of the menu will never change, I've made a static DataProvider for the whole class that populates each instances menu. I was hoping to populate the list with actual functions like so:
tmpArr.push({label:"Details...", funct:openDetailsMenu, args:""});
and then assign tmpArr to the DataProvider. Because the DataProvider is static the function that contains that code also needs to be static, but the functions in the array are non-static. At first it didn't seem like a problem, because when the user clicks on a menu item the drop down menu can call a non-static "executeFunction(funct, args)" on its parent. However, when I try to compile, the static function setting up the DataProvider it can't find the non-static functions being passed. If the compiler would just trust me the code would work fine!
The simple solution is to just pass strings and use a switch statement to call functions based on that, but that's big, ugly, inelegant, and difficult to maintain, especially if something inherits from this class.
The simpler solution is to just make the DataProvider non-static, but I'm wondering if anyone else has a good way of dealing with this? Making the static function able to see its non-static brethren?
Thanks.
OK, the basic reason for making things static is if you want to make it independant of an instance, for example the Math functions in as3 (you call Math.min() as opposed to var math = new Math(); math.min()...) this is useful for reference, repetetive calculation, simple actions (add 10 to x value) etc.
the problem with combining static and non static functionality is that when calling a static function, there is a possibility that the class has no instance at that point, or (in this case) that there is any reference to the function that would make sense in compilation (if a seperate class called the function, how would it reference openDetailsMenu?).
what you need to do is either go through getting function by name (object"functionname" works for example), make annonymous functions in your array or alternatively add a callback method to your static function something similar to this:
public static function doAction(object:Menu, event:String){
if(event == "details") object.openDetailsMenu() ;
}
all in all you are just adding layers of complexity that isnt really going to help. if you just add a class function and get them all to do the same action it is not taking more space or effort than if you are calling to a static function. you need to think about how and why the function is going to be used in (or out of) the class.
you could just store a static reference to the instance, in this case _instance. ( Kind of like a ghetto singleton ) just be careful not to call the static method before the class has been instantiated.
/// in your constructor define a static reference handle to the instance
public function ClassName(){
_instance = this;
}
public static function doSomethingStatic(){
var varValue = ClassName._instance.someInstanceVariable;
}