addressing a child from another class created by getdefinitionbyname - actionscript-3

I have a class "pagecompiler" which does the following:
if(page){
removeChild(page);
}
switch (appModel.currentPage){
case "Programma":
case "Winkelwagen":
case "Films":
case "Contact":
case "Reserveer":
var pageClass:* = getDefinitionByName("be.reynaertvincent.view.pages."+appModel.currentPage+"Page");
page = new pageClass();
addChild( page );
break;
}
so it creates a page depending on the switch, the names are (contactpage, filmspage, contactpage, etc.)
each of these pages extend from a class called "page".
And in "page" class I do the following:
contentBg = new ContentBg();
sidebarBg = new SidebarBg();
addChild(contentBg);
addChild(sidebarBg);
Now what I would like is to apply a tween on contentBg when I do the following in pagecompiler:
if(page){
removeChild(page);
}
but I can't seem to address contentBg from there. I tried:
if(page.contentBg){
tweenlite.to(page.contentBg,blablabla);
//removeChild(page);
}
but it doesn't get recognized. Anyone having any ideas?

I see a number of problems with your code. If you correct them, your problem should be solved:
You should stick to naming conventions: Class names should start with an upper case letter. So it should be Page instead of page. Otherwise, you have a member variable with the same name as a type - and potential compilation errors.
getDefinitionByName() is a costly way of instantiating a class in terms of performance, and since it also isn't type safe, you are dealing with a potential problem. Since you already know which classes you are going to instantiate, why not just make page an instance of Page and extend your switch statement:
private var page:Page;
// some code here
switch (appModel.currentPage){
case "Programma":
page = new ProgrammaPage();
break;
case "Winkelwagen":
page = new WinkelwagenPage();
break;
case "Films":
page = new FilmsPage();
break;
case "Contact":
page = new ContactPage();
break;
case "Reserveer":
page = new ReserveerPage();
break;
}
addChild( page );
If you make page an instance of *, as is implied above, you need to cast to the Page class in order to access its contentBg property. Something like this:
tweenlite.to (Page(page).contentBg, ....);
This last point, however, should already be solved if you follow the first two hints, since the compiler now knows that page is of type Page. It is also a nice example why the lower case naming mentioned above doesn't work, as page(page).contentBg is obviously ambiguous.

In your page class , you could create a public method:
public function removeFromStage():void
{
TweenLite.to ( contentBg , etc... {..... onComplete: remove})
}
//Called when the Tween is complete
private function remove():void
{
parent.removeChild( this );
//may come handy , don't forget to add a listener if you do this :)
dispatchEvent ( new Event ( Event.COMPLETE ) );
}
Then you can call it like this
if(page != null ){
//add Complete event listener if necessary...
page.removeFromStage();
}

Related

How to change one object into another object?

This is mostly a question about code design. What you see here is a very condensed version of the original code.
An example of the code is:
player.rest();
This makes the player sleep.
I have other methods such as walk, run, talk etc. which all work great. The one that is a problem is polymorph. It changes the player from a human object to another object. The solution I came up with is this:
class main
{
var human:Human = new Human;
var alien:Alien = new Alien;
var cow:Cow = new Cow;
var player = human;
enterframe loop{
//other code
if (player does something)
player.polymorph = "alien";
switch (player.polymorph)
{
case "alien":
player = alien;
break;
case "cow":
player = cow;
break;
//etc
}
player.update();
}
}
I want something that looks like this:
class main
{
var human:Human = new Human;
var alien:Alien = new Alien;
var player = human;
enterframe loop
{
player.polymorph(alien);
}
}
I know my original solution is the wrong way to go about things as it encourages spaghetti code. How could I do it differently? I don't mind a whole rewrite, but need an example to help push me in the right direction. I hope this makes sense and thanks for the help.
If the second one can work, what would the polymorph function look like?
I thought of making a class called player and changing what that extends, but to my knowledge that can't be done? Plus I would like to change the character to something already in game rather than a new object.
One solution to your problem would be using a single class, in this case, your Player class, and a finite state machine. You'd have a Player class, which can be set to different states, such as HUMAN, ALIEN, COW, etc. When a polymorph event occurs you update the Player's state, perhaps by calling an initState() method, and handle the logic for being a human, alien, cow, accordingly in whatever method updates your player.
Assuming the player has an update() method it could contain the following:
switch (state) {
case ALIEN:
// do alien stuff
case COW:
// do cow stuff
case HUMAN:
// do human stuff
}
Next, instead of handling the various polymorph states in a switch statement, your Player class could have a polyMorph method that takes a state as a parameter:
public function polymorph(newState:Int) {
state = newState;
initState(state); // You could even just call the initState method instead, and completely omit the polymorph method
}
Using a finite state machine here would eliminate the need for numerous objects.

How to choose which child class to instantiate dynamically

My current project is in as3, but this is something I am curious about for other languages as well.
I'm attempting to use a factory object to create the appropriate object dynamically. My LevelFactory has a static method that returns a new instance of the level number provided to the method. In the code calling that method, I am able to dynamically create the buttons to call the levels like so:
for (var i:int = 1; i < 4; i++) {
var tempbutton:Sprite = createButton("Level " + i, 25, 25 +(60 * i), start(i));
_buttons.push(button);
}
This code just creates a simple button with the given arguments (ButtonText, x, y, function). It's working fine. The buttons are created, and clicking on one of them calls this method with the appropriate argument
private function start(level:int):Function {
return function(e:MouseEvent):void {
disableButtons();
newLevel = LevelFactory.createLevel(level);
addChild(newLevel);
}
}
This is all working fine; I'm just providing it for background context. The question I have is this: Is it possible to dynamically choose the type of object that my static function returns? Currently, I have am doing it as follows
public static function createLevel(level:int):Level {
var result:Level;
switch(level) {
case 1: result = new Level1(); break;
case 2: result = new Level2(); break;
//etc
}
return result;
}
I should note that all of these Level1, Level2, etc. classes extend my base level class. (Yay polymorphism!) What I would like to do is be able to do something along the lines of
public static function createLevel(level:int):Level {
var result:Level;
var levelType:String = "Level" + level;
return new levelType();
}
Obviously it's not going to work with a string like that, but is there any way to accomplish this in as3? What about other languages, such as Java or Python? Can you dynamically choose what type of child class to instantiate?
Update:
import Levels.*;
import flash.events.*;
import flash.utils.*;
public class LevelFactory
{
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.' + 'Level' + level) as Class;
var result:Level = new ref();
return result;
}
}
Update/Edit: getDefinitionByName seems to be what I'm looking for, but it has a problem. It seems that the compiler will strip unused imports, which means that unless I declare each subclass in the code ahead of time, this method will get a reference error. How can I get around the need to declare each class separately (which defeats the purpose of dynamic instantiation)?
Yes, you sure can, and it's very similar to the string thing that you've provided. The only thing that you are missing is the getDefinitionByName method: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/package.html#getDefinitionByName()
You can generate whatever class name you want, and what this method does is that it searches for that class in it's namespace, and if it finds it - it returns it as a class:
var ClassReference:Class = getDefinitionByName("flash.display.Sprite") as Class;
var instance:Object = new ClassReference();
This piece of code will instantiate a Sprite. This way you can instantiate your classes without all those switches and cases, especially when you have to make a hundred levels :)
Hope that helps! Cheers!
Edit:
In your case, the code should be:
var ref:Class = getDefinitionByName('com.path.Level' + index) as Class;
var level:Level = new ref(); // it will actually be Level1 Class
Since Andrey didn't quite finish helping me out, I am writing up a more complete answer to the question after much research.
getDefinitionByName definitely has the use I am looking for. However, unlike its use in Java, you HAVE to have a hard reference to the class you want instantiated somewhere in your code. Merely imported the class is not enough; the reason for this is that the compiler will strip the reference from any unused import to save space. So if you import the package of classes you want to choose dynamically but don't have a hard reference to them, the compiler will de-reference them. This will lead to a run-time error when the program cannot find the appropriate reference to your class.
Note that you don't actually have to do anything with the reference. You just have to declare a reference so that it can be found at run-time. So the following code will work to eliminate the switch-case statement and allow me to dynamically declare which class I am using at run-time.
{
import Levels.*;
import flash.events.*;
import flash.utils.*;
/**
*
* Returns the requested level using the createLevel class
* ...
* #author Joshua Zollinger
*/
public class LevelFactory
{
Level1, Level2, Level3, Level4, Level5, Level6, Level7;
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.Level' + level) as Class;
var result:Level = new ref(); // it will actually be the correct class
return result;
}}}
The obvious downside to this is that you still have to have a hard-coded reference to every class that can be instantiated like this. In this case, if I try to create a Level8 instance, it will through a run-time error because Level8 is not referenced. So every time I create a new level, I still have to go add a reference to it; I can't just use the reference dynamically.
There are supposedly ways around this that I have not tested yet, such as putting the code for the classes in a separate SWF and importing the SWF at run-time or using outside libraries that will have different functionality. If anyone has a solid way to get a truly dynamic reference that doesn't require a hard coded reference anywhere, I would love to hear about it.
Of course, it's still a lot cleaner this way; I don't have a extensive switch case statement to pack all the levels. And it's easier and faster to add a reference to the list than creating a new case in a switch. Plus it is closer to dynamic programming, which is usually a good thing.

A function that deletes an instance by removing it from stage and nulling it does not remove it from memory

I have an issue with a function I use to delete an instance and replace it with another. Basically, it keeps the item in memory no matter what. Inside the object I have weak listeners and I null everything after it gets removed, but the function I run to check if it is still active tells me that it is (just an Event.ENTER_FRAME tracing some text, with a weak link).
Even when I removed everything from the instances I am loading, it still seems to stay in memory, according to my trace it still is. How do I completely delete something from memory more thoroughly than nulling it out after removing it from the stage? Am I not seeing something?
This is the function:
private function loadArea(inputArea:String)
{
//This is for a checker to make sure that areas only get loaded once.
currentRoom = inputArea;
//If the area currently loaded is not null, make it null now.
if(selectedArea != null) selectedArea = null;
//Null any data inside of the reference used to create the name of the new area.
areaReference = null;
//Grab the class using the input.
areaReference = getDefinitionByName(inputArea + "Area") as Class;
//Null the sprite used to house the class
areaSprite = null;
//Set the holder as a new instance of the desired class.
areaSprite = new areaReference() as Sprite;
//If the selected area is still not null for some reason,
if(selectedArea != null)
{
//Remove the area from the container...
areaContainer.removeChild(selectedArea);
//...and nullify it.
selectedArea = null;
}
//Set the current area as the newly created instance.
selectedArea = areaSprite;
//If the area is not the "Game", load in the assets one way,
if(inputArea != "Game") selectedArea.construct(areaAssets);
//otherwise do it another way.
else selectedArea.construct(newScreenData,apiServer,cdnServer,areaAssets);
//This is for a checker that fades out the screen, which it needs to fade back in soon.
newScreenData = null;
//While the container for areas has any areas inside of it, remove them.
while(areaContainer.numChildren) areaContainer.removeChildAt(0);
//...then add the new instance area to the container.
areaContainer.addChild(selectedArea);
//...then let all the parts of the game know that a new area has been laoded in.
Global.echoEvent.echo("gameBootUp","playAreaIn");
}
The memory is actually released when Garbage Collector will find and erase an orphaned instance of yours. Before that, your memory usage will state there is an instance in memory. There is no way to force garbage collection, calling System.gc() only "instructs" Flash to run it, it might not obey. So, you have done what you had to, let it be.
Removing all references including stage and nulling an object is all it takes to free up memory.
If the object is not being released then you are missing something or doing something incorrectly or out of sequence.
Carefully go through your code, making sure you identify where objects are being referenced so you can remove them.
Looking at your example code:
if(selectedArea != null) selectedArea = null;
Here you are making sure that the selectedArea is null.
But immediately after you are testing selectedArea again
(you know it is null so this block is never used)
if(selectedArea != null){
//Remove the area from the container...
areaContainer.removeChild(selectedArea);
//...and nullify it.
selectedArea = null;
}
In every language its VERY DIFFICULT to "clear" memory... even HTML. That being said... try to reduce your memory footprint.
Correct Null:
1. remove all event listeners
2. remove all children
3. remove all mapped/referenced methods/parameters
4. set the class object to null
In most cases this is all you need to do, the WAY you do it will determine if the object gets cleared. Consider the following situation.
You have a custom sprite class (MEMORY FOOTPRINT #1) that has a mapped property (mapping happen when one class object references another). Once you map/reference one object to another = MEMORY FOOTPRINT #2. Adding events = MEMORY FOOTPRINT #3, etc and so on.
Custom Sprite
import flash.display.Sprite;
class CustomSprite extends Sprite{
private var _mappedProperty:Object;
public function addMapping(map:Object):void{
_mappedProperty = map;
}
public function finalize():void{
_mappedProperty = null;
}
}
Assuming we're using CustomSprite in many other methods, lets look at some common ways of removing the ojbect.
INCORRECT - in this situation [objToRemove] was not set to null to free its memory:
var objToRemove:CustomSprite = new CustomSprite;
function doSomething(referenceObj:CustomSprite):void{
var methodObj:CustomSprite = referenceObj;
//CRAZY LINES OF CODE
methodObj = null; //frees memory from [doSomething::methodObj]
referenceObj = null; //frees memory from [doSomething::referenceObj]
//objToRemove is not cleared and will remain in memory
}
INCORRECT - in this situation [objToRemove] has a reference object so it will not clean until the reference is removed:
var objToRemove:CustomSprite = new CustomSprite;
var mappedObject:Sprite = new Sprite;
objToRemove.addMapping(mappedObject);
objToRemove.addEventListener(Event.ENTER_FRAME,onEnterFrame);
//CRAZY LINES OF CODE
//remove all children
while(objToRemove.numChildren > 0){
objToRemove.removeChildAt(0);
}
//remove all event listeners
objToRemove.removeEventListener(Event.ENTER_FRAME,onEnterFrame);
//this will NOT work
objToRemove = null;
//reason is objToRemove has a reference object of [mappedObject]
//[mappedObject] is not a child so it needs to be removed manually
//from WHITIN the CustomSprite class using [CustomSprite::finalize()]
Ok... breath... the correct way is actually simple.
CORRECT - here we are using [Dynamic] objects rather than [Static] class objects, this is considered Object Mapping:
//think of this as a global list of objects
var objPool:Dictionary = new Dictionary;
//create a pool reference
objPool['poolObj'] = new CustomSprite;
//CRAZY LINES OF CODE;
//do the normal [null] process
//both of these will work
objPool['poolObj'] = null;
//or
delete objPool['poolObj'];
SUPER ADVANCED CORRECT - no example provided, I have to get back to work lol...
1. Take a ByteArray clone of the class
2. User a Loader to construct the ByteArray as the class, rather than using "new"
3. When finished... unload/remove the loader
4. EVERYTHING will clear... thats how a Loader works!
(Not getting into why and how... too long of an explanation)
Although this works flawlessly... its not generally accepted or suggested in a work environment.

Why not use a basic approach to registering a click event for a view ( say ButtonView)

Let's say there is a buttonView ( ie. a simple button) . To register a click in my rootView , i do as follows :
generally followed 1st Approach :
class RootView
{
var buttonView:ButtonView ;
....
buttonView.addEventListener( ButtonView.CLICK, buttonView_click ) ;
...
}
I wonder, why not a basic approach ( 2nd Approach ) is followed like this :
class RootView
{
var buttonView:ButtonView ;
buttonView.setFunction( buttonView_click ) ;
}
class ButtonView()
{
public function setFunction( someFunction:Function)
{
viewFunctionArray_Arr.push( someFunction )
}
public function onClick()
{
// remove dispatchEvent from here , instead
for ( var i=0; i< viewFunctionArray_Arr.length; i++)
{
//calling the registered functions of all the views one by one
viewFunctionArray_Arr[i] ( ) ;
}
}
}
If the explanation is regarding "Loose Coupling", then i wonder, how "1st approach" is loose coupling, because afterall in the 1st approach, we are using instance "buttonView" in the views that need it. So rootView needs to know about buttonView. Isn't it ?
When you use addEventListener() Flash player is doing something very similar to your second approach.
One reason I would use addEventListener() instead of passing a reference to a function is to avoid potential memory leaks:
addEventListener() has some optional parameters. The last one specifies to use a weak reference: addEventListener(MouseEvent.CLICK, eventHandler, false, 0, true). Weak references do not prevent an object from getting garbage collected.
There are cases when an object is disposed that you need to clean up things (ie: remove event listeners), and using a weak reference can mitigate some of the clean up tasks.
In regards to loose coupling, I think both approaches are the same. In one case the parent object needs to know that the child view dispatches an event. In the other, the parent needs to know that it should pass an event handling function to the view. These are both loosely coupled in my opinion, there is nothing stopping you from using the child view somewhere else.

How to remove this unrelated block of code, that checks child's availability in the container

This is my code on button click. It adds myView to the container. And takes care of fact that all previous instances get removed before adding again. I wanna know, if there is some better way of making this "check" ? I especially wanna have some way to remove the 1s 7 lines of this code ( within comments). It seems like an unrelated block of code in the normal code-flow.
function button_CLICK(e:Event)
{
///////////////////////////////////////////////////
if ( myView!= null)
{
if ( contains(myView))
{
removeChild(myView) ;
}
}
/////////////////////////////////////////////////////
myView = new myView("hello")
addChild(myView);
}
it would depend on what the object myView consists of. if, for example, it has its own event listeners you would want to use the condition check to clean up the object before disposing of it so there are no memory leaks.
function button_CLICK(e:Event):void
{
if (myView && contains(myView))
{
//clean up myView
removeChild(myView);
}
myView = new myView("hello");
addChild(myView);
}
however, if myView is a simple display object that doesn't contain any leakable references or event listeners than you could simply reassign the variable to overwrite the previous.
keep in mind that addChild() places the added display object on top above all others. but regardless if that's something you'd like to avoid or not, i believe it would be better to assign a default or empty view once instead of addChild(myView) each time the button is clicked.
function button_CLICK(e:Event):void
{
myView = new myView("hello");
}
i assume your code is for for demonstration because new myView("hello") is going to always be the same object? unless perhaps this object relies on dates or timers to make instances different from previous constructs.
The checking code you have could create a memory leak as you don't null the previous myView before creating a new instance. NB. It's good practice to begin the name of your classes with an uppercase letter.
Your code also seems to allow for the button_CLICK method to be called when myView has been added. Perhaps consider hiding the button when the view has been added.
You could just have one instance of the MyView Class instantiated. Then rather than having to null and create a new instance each time, just add the same one and add an init function that will accept the initialising parameters. e.g.
var myView = new MyView();
function button_CLICK(e:Event)
{
if ( contains(myView))
{
removeChild(myView) ;
}
}
myView.init("hello");
addChild(myView);
}
I don't think you have much of an option, based on the (IMO) flawed implementation of DisplayObjectContainer. If you're feeling feisty, you can "fix" removeChild to see if the child is there before calling the super method, rather than simply throwing an error, as Adobe thought was the appropriate solution.
Your code above will still run, but it will be in the removeChild override.
Your commented bloc of code is necessary only for the first call to button_CLICK (you sould rename this method : buttonClick).
After the firts call, this bloc of code is unnecessary.
In order to remove this code myView should be not null, and an instance of MyView should be added on the display list.
var myView : MyView;
function init(){
myView = new MyView("FAKE");
myView.visible = false;
addChild(myView);
addEventListener("click",click);
}
function click(e:MouseEvent):void{
removeChild(myView);
myView = new Child("REAL");
addChild(myView);
}
Try:
if(myView != null)
myView.parent && myView.parent.removeChild(myView);