AS3 How to delete an object for good? - actionscript-3

I create a new Shape, make a listener that starts a function when an object hits the shape, the function clears graphics of the shape, removes listener and deletes the child. But it looks like it leaves some ghost that still triggers when my object comes to it's place. I'm not sure why it happens, I thought clear+remove should make it impossible to hitTest. But somehow children still pile over one another and the last created one triggers the function. So if you understand, please give me a hint, if not please tell me in general what is a way to delete a created Shape for good, so it didn't count existing?
A bit of details. Hitting the shape must start a function that deletes it and creates a new shape of the same name that has a different function that does the same etc. so the current function must be the one I called for, not the last one, whose listener I deleted. Making new shape each time may overload memory, besides I do clean up every time. Did I forget anything?
fun0(){
var bob:Shape = new Shape();
addChild(bob);
bob...drawRect...
addEventListener(...,fun1)
function fun1(){ if(hitTest...){
bob...clear();
removeChild(bob);
removeEventListener(...,fun1)
fun2();
}
}
fun2(){
var bob:Shape = new Shape();
addChild(bob);
bob...drawRect...
addEventListener(...,fun3)
function fun1(){ if(hitTest...){
bob...clear();
removeChild(bob);
removeEventListener(...,fun3)
fun0();
}
}
etc., it only sees fun3 after I once trigger it even though I deleted the listener. Again, I do move object away, it doesn't hitTest anymore, then clear+remove. If you don't understand or cannot help, please don't flame, just ignore this, I need help, not the info that I'm not as smart as you are. Thank you.

first of dont declare functions inside of functions. then use weak reference for event listners.
// params: eventName, listener, capturePhase, priority, useWeakReference
someObj.addEventListener("eventName",myFunct,false,0,true);
you musst get rid of any references to the object, then set it to null
removeChild(bob);
bob= null;
after that GC will collect it!
edit:
if you keep doing the hitTest, like on enterFrame for example. check if the object is on the stage, by checking its .stage property.
if(bob.stage)
{
if(hitTest ...){};
}

Related

Square bracket notation to dynamically load library items?

So I've spent an embarrassing number of hours trying to save myself a few minutes and make my code a bit neater, and Google has produced nothing, so now I come crawling to stackoverflow. The problem is with square bracket notation + library items:
So let's say you have a MovieClip called "myMC_5", and you want to set its X position to 0..
myMC_5.x = 0;
..and if you don't want to hard-code the name of the MC but instead you want one line of code to move a specific MovieClip based on a variable, you could do something like this:
var selectMC = 5;
root["myMC_"+selectMC]x = 0;
..and this will have the exact same effect as myMC_5.x = 0, except that this time you must specify the location ("root" or "this" or something).
THE PROBLEM:
I'm working on a game in which the graphic for the background is loaded from the library, and it's different for each level. The initial loading of the vector from the library looks like this:
private var land:vector_land0 = new vector_land0();
..and this works fine, but it only loads that one specific vector. There should be about 30 or more. I'd like to just have 1 line of code in the constructor to load any of them, based on a variable which keeps track of the current level, like this:
private var land:["vector_land"+theLevel] = new ["vector_land"+theLevel]();
..but that doesn't work. I get syntax errors ("expecting identifier before leftbracket") because you need to specify the location of the object, like in the first example:
root["myMC_"+"whatever"].x = 0;
..but this library item has no "location". So, how the heck do I dynamically load a vector from the library? It's not "on the root", or anywhere else. It has no location. I refuse to believe that the standard method for accomplishing this is to create 30 different classes or write a giant block of code with 30 "if" statements, but searching Google has found nothing. :(
It sounds like you're looking for getDefinitionByName(), which you could use to do something like this:
import flash.utils.getDefinitionByName;
private var LevelVectorClass:Class = getDefinitionByName("vector_land" + theLevel) as Class;
private var land:Object = new LevelVectorClass();
This is a horrible way to solve the situation, I don't recommend using square brackets anywhere but arrays. I recommend you putting the "lands" into a LandContainer MovieClip, each frame of that MovieClip would container 1 graphic. It is much cleaner, and you could create constants to store the "identity" of the frames.Example:
var land:LandContainer = new LandContainer();
land.gotoAndStop(FIRST_LEVEL); //FIRST_LEVEL is a constant integer
You can even reuse this LandContainer instance because you can set it's visibility, remove from the display list, set it's frame to the next level without creating another instance. On second thought, I would write a wrapper for this class. Aka link it to your own class which extends the MovieClip class and create custom functions, fields... etc..
This dynamic thing is horrible, hard to maintain, and not efficient at all. Don't know why did not they delete it from AS3... they should have.

AS3 Nullify Object through Reference

I have seem people asking this question, and getting an answer saying that this is impossible in AS3.
So I have a function called destroymc:
function destroymc(mcname:MovieClip):void{
mcname = null
}
This doesnot destroy the movieclip I want to destroy. Because it is passed by reference.
Is there really no way to make this work?
thanks.
Hm, as far as I know, to 'destroy' a MovieClip in AS3.0, you need to get rid of all references to it, and you need to remove it from the parent. Then the GC can collect it.
If you have an array of MovieClips, then something like this could work.
var mcs:Array;
// populate array
// to destroy the first one
mcs[0].parent.removeChild(mcs[0]); // remove the first element from its parent
mcs.shift(); // remove the first element in the array
And so if you want in in a function, you could pass the array to it
function destroymc(mcs:Array){
mcs[0].parent.removeChild(mcs[0]);
mcs.shift();
}
I'm using an array here because you need to use functions like this with dynamically generated MovieClips and entity managers and stuff. I hope this helps.

AS3 Using Many Event Listeners Causing Problems, How to Reduce?

Confusing title, my bad.
Basically, I have a list of names. Looping through, I add a MovieClip, Set 2 properties to it, the name, and an ID. The MovieClip is at the same time made to function as a button and I add 4 listeners, mouse up, over, down, or out. I do this with every name. The function each one is set to is the same.
EX: enemyButton[i].addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
The enemyID turns up "not valid property," time to time when I click, it doesn't crash at all, but sometimes I have to hit the button a few times.
I have narrowed the problem down to having to be caused by the listeners.
The function as simple as:
EX: function mouseUpHandler(e:MouseEvent):void { enemySelected(e.target.enemyID); }
My question is, is too many listeners likely to be the problem? and how can I reduce them?
Here's a snippet of the loop:
var C:Class = Class(getDefinitionByName(enemies[i]));
var c:* = new C();
c.gotoAndStop(1);
enemyButton[i].enemyID = i;
c.name = "select" + i;
c.enemyID = i;
trace(c.enemyID);
enemyButton[i].addChild(c);
enemyScroll.addChild(enemyButton[i]);
enemyButton[i].enemyName.text = info[i][Const.NAME];
enemyButton[i].setChildIndex(enemyButton[i].getChildByName("enemyName"), enemyButton[i].numChildren-1);
Thanks.
If enemyButton is a MovieClip (created via attachMovie, maybe) and not strongly typed as a EnemyButton class, then the ID property becomes dynamic. In this situation, if your list of names contains incorrect data (missing ID field, maybe), then the ID property will remain undefined on some instances of the MovieClip.
You can check the list of data used to generate movie clips. You can run into the same error if you have blank lines in your data.
This has nothing to do with event listeners.
So you just want to generate a bunch of buttons with unique properties and know what button was clicked last. Generally it is very bad idea to implement button logic outside button object. Why? Because you work with object oriented language. Good news is that you work with as3 and it treats functions as objects, so you can assign function to var like this:
var callback:Function = function(name:String, enemyId:int){ /*do something*/ }
And.. you can pass function as a parameter to another function
function setCalback(func:Function){}
button.setCallback(callback);
So, what you really need is to create your own button class, add listeners inside it, add handlers(static handlers will reduce memory usage) and pass callback function to it, that will be called when user clicks button.
Don't mean to spam this much but this was easily fixed, though the responses might have been a better method.
I just had to change target to the currentTarget, that then allowed clicking anywhere on the "button" to work. Whereas before the target varied from childs added to it.
So, solved.
Thanks for the help.

How to copy Input Text Value into Dynamic Text that is in a MovieClip1 Inside a MovieClip2

Current my code is as such
setcustomlocation.addEventListener(MouseEvent.CLICK,customlocation2);
function customlocation2(e:MouseEvent):void
{
locationinput.text = FlashingWon.Won1.name.text;
}
I'm trying to make it such that it would copy the input text field values into a dynamic text. However, it throws up the error that
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at main/customlocation2()[main::frame1:9]
Which can I only assume that it is not able to communicate with the dynamic text field in the movieclip within another movieclip.
First, you can be 100% sure that it CAN communicate with the dynamic TextField in the MovieClip.
From you description I understand that you wish to copy from input into dynamic. But from your code I see that you take from the dynamic into the input, please check that out:
// This will copy in such a way: input <= wonText
locationinput.text = FlashingWon.Won1.name.text;
// This will copy from input
FlashingWon.Won1.name.text = locationinput.text;
Anyhow, the error that you get has nothing to do with this, it's rather like you already noticed, that one of your TextField is not 'found'. For this I recommend you a best practice: To create instances of the objects you want to use and populate them from the stage through the getChildByName method. This way you will promptly now (specially if you do this on construction or init) if you had any misspelling or miss structure on the childs you want to get.
like so:
var inputText: TextField;
var dynoText: TextField;
In your Constructor or else where at a soon level, give to your vars the proper value:
inputText = getChildByName('locationinput') as TextField;
dynoText = FlashingWon.Won1.getChildByName('name') as TextField;
This way you will soon enough know if one of this 2 textFields were not found under the object you give, and you have only one place to miss spell it. Also you will get code completion on your TextFields.
Finally the copy text part:
dynoText.text = inputText.text;
Hope it helps.
Alright, sorry for the delay, I was out on holidays.
I have opened your example and can see where the problems are:
1) You are trying to reach the FlashingWon when initiating the dynoText on the FIRST frame like so var dynoText = FlashingWon.Won1.getChildByName('name_txt'); BUT the FlashingWon element is ONLY available on the FIFTH frame. Meaning that you cannot refer to it quite yet, unless you add it on the first frame and make in invisible till the fifth frame. (You can make it visible in the goto1 function if you wish after the goToAndStop(5) line)
2) You called the TextField on the Won1 element 'name' which is a restricted sting in AS3, so change it to name_txt or label if you wish and it will work.
Let me know how it worked.

Animation flickering problem

This is the game loop in my code and the drawing code:
float frames_per_second = 60;
display_timer = al_create_timer(1/frames_per_second);
queue = al_create_event_queue();
al_register_event_source(queue, al_get_timer_event_source(display_timer));
al_start_timer(display_timer);
while(!end_game)
{
ALLEGRO_EVENT event;
al_wait_for_event(queue, &event);
if(event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) break;
if(event.any.source == al_get_timer_event_source(display_timer))
{update_display();}
update_input();
}
void update_display()
{
al_clear_to_color(al_map_rgb(255, 255,255));
draw_objects(); //this is just an al_draw_bitmap() call
al_flip_display();
}
The animation created by moving objects on screen flickers, I'm surprised by this since I write to the back buffer of the screen thus I expect double buffering. What can I do to correct the flickering? Thanks.
Unrelated to the problem, you can check a timer by looking for the ALLEGRO_EVENT_TIMER event. You can use event.timer.source to check which timer it is, if you have more than one.
I think the main problem here is that you are drawing the graphics at 60fps but you are updating the input at an unlimited rate. This is actually backwards. You want to update the input at a fixed rate. You can draw the graphics as often as you like... although it makes no sense to update the graphics if nothing has changed.
So it should look something more like:
if(event.type == ALLEGRO_EVENT_TIMER)
{
update_input();
update_display();
}
However, this does not implement frame skipping if things get too slow. What you should do is set up the timer in a dedicated queue (that contains no other event sources). Then as long as there are events sitting in that dedicated timer queue, update the input.
Then update the display, assuming you've processed at least one tick. So you may, if things get too slow, do multiple input updates per drawn frame.