For some time now I have been making a very easy game for iPhone in flash using as3.
Recently I came in contact with a small problem, which is why I am posting this!
The problem:
I have a superclass from which everything derives. In the superclass I initiate and place an Object on stage.
1. var myObject:typeA = new typeA();
2. stage.addChild(myObject);
As you can see this object follows the class 'typeA' which, ocf, has its own actionscript file. Inside of this file I have declared a global variable of type string.
What I want to do is change the varbiable on the new object from the superclass. Therefor I tried as following:
1. myObject.myVariable = 'someSortOfString';
Unfortunatly it didn't work and so I wonder how to do this; change a subclass' variable from the superclass.
You need to declare the variable that is being accessed from the subclass as protected (Or public), by default the variable is private so only accesible by the superclass.
e.g. protected var myObject:typeA = new typeA();
BTW did you mean change the superclass variable from the subclass instead of "change the subclass variable from the superclass"?
Related
I've got a little problem.
I'm trying to remove a child called in an other class.
I've called "viseur" in my Engine class like that :
private var viseur:Viseur;
viseur = new Viseur(stage);
stage.addChild(viseur);
Now, in my Puzzle.as class I'd like to removeChild(viseur) when my puzzle is complete.
How do I do to do that ?
I've tried :
Engine.viseur.stage.removeChild(viseur);
But it is not working... (and either Engine.viseur.removeChild(viseur) )
Anyone know how could I do that ?
Thank you very much,
There are two issues at play here:
The call Engine.viseur.stage.removeChild(viseur); does not work because the variableviseur is private to the Engine class. If you wish for it to be accessible to other classes, you need to make it public.
However, the other issue with this is that the viseur variable is not a static variable either. So accessing it like this, Engine.viseur.stage.removeChild(viseur); is still incorrect even if you fix the variable to be public. if you wish to access it via another class, you can either 1) pass a reference of the instance of the Engine class to your other class so that it can access the viseur variable or 2) make the viseur variable a static variable, but remember if you do this, that means you can only ever have one Viseur object named viseur.
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.
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.
I am currently reading the Actionscript 3 Bible and the author shows an example in which a custom eventDispatcher class is created. When the class is later used in the code it's called as such:
//Thermometer is a custom eventDispatcher that extends the eventDispatcher class.
var thermometer:Thermometer = Thermometer(event.target); //why no new keyword?
I'm a new programmer and I was curious if someone could explain how this works? Why is the new keyword omitted? When you omit the new keyword on a class that extends another class is it essentially just calling the superclass(eventDispatcher) constructor?
This is not creating a new instance, but is casting.
What that means is that the target property of the event object is cast as Object by the Event class, and so to tell the compiler that the target is actually an instance of the Thermometer class, it is cast in this style. You could also write it like this:
var thermometer:Thermometer = event.target as Thermometer;
The two essentially mean the same thing, but when using an editor that supports code suggestion/completion for custom objects, the Class(something) notation is preferred because it will enable the editor to provide suggestions based on your cast.
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().