Is this the correct way to remove a timer? - actionscript-3

I'm not sure if the way I did makes the garbage collector remove the timer. Here are my two functions:
public function newWave() {
var callTimer:Timer = new Timer(800);
callTimer.start();
leftToSpawn = 4;
callTimer.addEventListener(TimerEvent.TIMER,waveCall);
}
public function waveCall(e:TimerEvent) {
leftToSpawn--;
if(leftToSpawn <= 0){
e.target.stop();
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE,waveCall);
}
spawnEnemy();
}
Thanks

To remove an event listener you need to remove it with the exact same signature.
If you do:
.addEventListener(TimerEvent.TIMER,waveCall);
Then you need to use the same event type and function to remove it:
.removeEventListener(TimerEvent.TIMER,waveCall);
Using TimerEvent.TIMER_COMPLETE here will try to remove a listener that doesn't exist, which is silently ignored.
Using target here is ok, for other listener types you may need to use currentTarget, which is always the object the listener got added to. For example in a mouse click event, target could be a child of a MovieClip and without any listeners.

Moreover, timer already has ability to repeat specified number of times and the correct code will be like this:
public function newWave() {
var callTimer:Timer = new Timer(800, 4); //repeat 4 times
callTimer.start();
callTimer.addEventListener(TimerEvent.TIMER,waveCall);
}
public function waveCall(e:TimerEvent) {
spawnEnemy();
}

just change:
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE,waveCall);
to
e.target.removeEventListener(TimerEvent.TIMER,waveCall);

Related

Add event listener to the items in an array

I have an array with movieclips (r1, r2 etc) which are placed on stage dynamically every time there is a hittestobject. I want to add event listener to those movieclips in the array so that every time there is a click on them the last item to be removed. I have this code but it seems to be wrong because only the first element of the array is remove. Can you please help me?
var counter:int = 0;
function releaseToDrop(e:MouseEvent):void
{
Star(e.target).stopDrag();
if (Star(e.target).hitTestObject(target))
{
removeChild(Star(e.target));
var replace:Array = [r1,r2,r3,r4,r5,r6,r7,r8]
addChild(replace[counter]);
counter = counter+1;
}
else
{
trace("No collision.");
removeChild(Star(e.target));
}
}
replace[counter].addEventListener("click", bindClick(o));
function bindClick(o)
{
replace.splice(replace.indexOf(replace[counter]),1);
trace(counter);
}
For one, your bindClick function is inside the releaseToDrop function. This way that function will not be called. Also, try to use the static refence for the click event, so MouseEvent.Click instead of "click".
For your convenience, trace the current state of "counter" in the bindClick function, so you know what element will be removed. You will probably then find out where the flaw is in your logic.
A wise thing to to is also to check counter is not outside the scope of your array. So in bindClick, check for counter < replace.length. (for that the replace array of course has to be defined outside of the function scope of releaseToDrop.
Hope this gets you working in the right direction.
For future references adding addEventListener to the elements of an array so that every time there is a click on them the last item to be removed can be done by this code:
import flash.display.Sprite;
var replace:Array = [r1,r2,r3,r4,r5,r6,r7,r8]
var counter:int = 0;
// Every time there is a hittestobject an element of the array is placed on stage
function releaseToDrop(e:MouseEvent):void
{
Star(e.target).stopDrag();
if (Star(e.target).hitTestObject(target))
{
removeChild(Star(e.target));
addChild(replace[counter]);
counter = counter+1;
trace(counter);
}
}
// The first element of the array has an eventListener and when is clicked the last item of that array shown on stage is removed
var yourSprite:Sprite=new Sprite();
replace.push(yourSprite);
replace[0].addEventListener(MouseEvent.CLICK, myClickHandler, false, 0, true);
function myClickHandler (e:MouseEvent):void
{
counter =counter-1;
if (contains(replace[counter]))
removeChild(replace[counter]);
trace(counter);
}
Can you add an event listener to every movie clip that you spawn, then in each event handler, add a conditional to check if the clicked movie clip is the last item, then remove it (or do whatever you need to to it).
You should also accept Chicken's advice.

AS3 delete object when clicked

I want to set an object to null when it's being clicked and I'm trying to implement this code:
public function onClick(evt:MouseEvent):void{
var thisPatient = evt.target;
thisPatient = null;
}
However, the element is still on the stage.
public function onClick(evt:MouseEvent):void{
var thisPatient = evt.target;
(thisPatient as DisplayObject).parent.removeChild(thisPatient);
//or if thisPatient is this
parent.removeChild(this);
}
But it's bad practive to allow children to remove itself. More right solution is dispathing event because parent must decide remove or not remove child.
public function onClick(evt:MouseEvent):void{
dispatchEvent(new Event("removeMe", true));
}
//parent's code...
child.addEventListener("removeMe", removeHandler);
Setting it to null doesn't suffice. You also have to remove it from its parent container using removeElement() or removeChild() depending on what kind of container you're using.
You just have to do removeChild(thisPatient) and if you put the object inside another object you have to do parent.removeChild(thisPatient)!
In my experience, Register for a events object, you must remove all of the events. all events will be removed completely manually. removeChild on the object that will not release all of the events. the removeChild but, finely memory leak occurs. This is because you did not remove the event. Before you remove an object, you must remove the event.

remove ENTER_FRAME EventListener from inside this as3

This is my code in Flash/AS3, in main class.
addEventListener(Event.ENTER_FRAME,function(e:Event){
if(findObject == true){
// I want to remove this ENTER FRAME
}
});
try this:
e.currentTarget.removeEventListener(e.type, arguments.callee)
You shouldn't be doing what you do in the code above.
The mgraph's code has a tiny chance of failing to work as advertised if the currentTarget of the event doesn't have a removeEventListener() method (possible, but very unlikely). From the compiler standpoint though you will be trying to dynamically resolve the method on a generic object which is error prone and should be handled with care. This is hazardous because it shows that the programmer "did not know" what kind of object was she expecting to handle and worked by assumption. Assumptions are great for finding a solution but are equally bad for implementing one.
If you thought of optimizing something in the way you did it, then, just FYI this actually creates a unique (redundant) name in the symbol table (in the compiled SWF file) which causes worse compression of the SWF.
If you are doing this as a matter of experiment, this is fine, but you should avoid such code in real life projects.
One more thing to be aware of: comparison to true constant is 100% useless. If such comparison makes any sense at all (i.e. findObject may evaluate to false any time), then if (findObject) { ... } is equivalent but shorter version of your code.
Last thing, hopefully, the anonymous function is missing return type declaration. It won't really change much in your example, except that you will get compiler warning. Omitting type declaration is, in general, a bad style.
EDIT
public function addEventListener(type:String, listener:Function ...):void
{
this._listeners[type].push(listener);
}
public function dispatchEvent(event:Event):void
{
for each (var listener:Function in this._listeners[event.type])
listener(event);
}
public function removeEventListener(type:String, listener:Function, ...):void
{
delete this._listeners[type][listener];
}
Suppose you actually want to implement IEventDispatcher (instead of using another EventDispatcher - you may have your reasons to do so, one such reason is that native EventDispatcher generates insane amounts of short-lived objects - events, and you may want to reduce that.) But there is no way you can replicate event.target or event.currentTurget in your code because you can't access the object owning the method, so, you would leave that out.
Another example:
public class SomeEvent extends Event
{
private var _target:NotEventDispatcher;
public function SomeEvent(type:String, someTarget:NotEventDispatcher)
{
super(type);
this._target = someTarget;
}
public override function get target():Object
{
return this._target;
}
}
This is something that I actually saw in real world, this was used in either Mate or similar framework to sort of "anonymously" connect all event dispatchers to a single static instance of some "mothership event dispatcher".
I don't necessarily justify this approach, but, technically, nothing stops you from doing either one of these. What I was saying in my post above is that in certain situations the language promises you things, like, if you did:
var dispatcher:IEventDispatcher;
try
{
dispatcher = IEventDispatcher(event.currentTarget);
// now you can be sure this object has removeEventListener
dispatcher.removeEventListener(event.type, arguments.callee);
}
catch (error:Error)
{
// but what are you going to do here?
}
But the most common case would be you subscribing to a bubbling event, in which case, you don't know whether you want to unsubscribe from event.target or event.currentTtarget - because you don't know which one is that you are listening to.
I agree with wvxvw.
Another way to approach your problem is to have a variable to control the "state" of your ENTER_FRAME event:
private var _state:String;
private function init(e:Event):void {
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event):void {
switch(_state) {
case "play":
// do play stuff
// when you want to pause
// goToPause();
break;
}
}
// you can call the method below from a button or whatever you want
private function goToPause():void {
_state = "pause";
// do some stuff here
// when you are done, switch "_state" back to "play"
}
In this example, you keep listening for ENTER_FRAME, but it only does things when the _state variable is set to "play". You can also remove the event listener in the goToPause method:
private function goToPause():void {
_state = "pause";
removeEventListener(Event.ENTER_FRAME, loop);
}
However, the nice thing about using the "_state" to switch things is that you don't end up having a mess of addEventListeners and removeEventListeners (which is what can happen depending on how complicated your loop gets) that you have to keep track of.
You should not use anonymous function call if you would like to remove listener some time later.
public function main():void
{
//...
//some method, where you add event listener
//...
//adding enterFrame event listener
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
//...
}
private function enterFrameHandler(e:Event)
{
if(findObject) // " == true" is not really necessary here.
{
// removing enterFrame listener:
this.removeEventlistener(Event.ENTER_FRAME,enterFrameHandler);
}
}
Just for a completeness with the other techniques mentioned here, the function you are creating is a unbound closure, so you can also leverage that concept to reference both your function and dispatcher.
var callback:Function;
var dispacher:IEventDispatcher = this;
addEventListener(Event.ENTER_FRAME, callback = function(e:Event){
if(findObject == true){
dispacher.removeEventListener(Event.ENTER_FRAME, callback);
}
});
Normal closed-over variable rules apply.

checking if child exists

Hello i have a function as following:
private function seatClickHandler(e:MouseEvent):void{
var check:Check = new Check();
if(e.target.contains(check)){
e.target.removeChild(seat);
}else{
e.target.addChild(check);
}
}
basicly i want to check if e.target contains a child called check. If it does i want e.target to remove the child, else i want to add the child. But the method i tried doesnt seem to work although i think this is the way to go. Any suggestions?
When you declare your Check object, Actionscript creates a reference code for that specific object.
So the first time your code is run, your Check object could be given a reference of #c0ecc29. Your if statement checks to see if #c0ecc29 is a child component of target. It won't be, so the Check object with reference #c0ecc29 is added to target.
The second time the clickHandler is called, a new instance of the Check object is created which will have a new reference id. Your target has the original Check object with the #c0ecc29 reference so it won't get removed.
The correct way to get this working depends on what target is (DataGrid, Group, etc.).
EDIT:
Based on your comments, I would try something like this. It checks to see if the Check object is a child of target and adds it if needed. Then when the Check object is clicked, it will toggle its visibility.
public var check:Check = new Check();
private function seatClickHandler(e:MouseEvent):void
{
if(!e.target.contains(check))
{
check.addEventListener(MouseEvent.CLICK, check_handleClick);
e.target.addChild(check);
}
}
protected function check_handleClick(event:MouseEvent):void
{
check.visible = !check.visible;
}
If you need to actually remove the Check object from target instead of just changing its visibility, you could try this:
public var check:Check = new Check();
private function seatClickHandler(e:MouseEvent):void
{
if(!e.target.contains(check))
{
e.target.addChild(check);
}
else
{
e.target.removeChild(check);
}
}
If the child is named 'check' then you should be able to use getChildByName(). See flash.display.DisplayObject.name
If you happen to have the child in memory, you can use getChildIndex()
check is a new object in the scope of that function, so it will not be a child of the event target.
What you want to do is declare check as a global variable (And also cast target as DisplayObjectContainer).
e.g.
private function seatClickHandler(e:MouseEvent):void{
if((e.target as DisplayObjectContainer).contains(check)){
(e.target as DisplayObjectContainer).removeChild(seat);
}else{
(e.target as DisplayObjectContainer).addChild(check);
}
}
However I'm not sure if this is exactly what you want to do (There can only be one check). A better approach would be to have a function (maybe toggleCheck) on the target, and have that display object responsible for rendering the check (And removing it)
This worked perfectly fine for me in my situation:
if(possibleChild.parent == holder)
holder.removeChild(possibleChild)
It may or may not be exactly what you're looking for.

Is there a way for listening for changes in flash.display.DisplayObjectContainer numChildren property?

I want to run some code whenever a DisplayObject is added as a child to a DisplayObjectContainer.
Or to put in other words, to catch the addedToStage event of all DisplayObjects, even ones I don't know about.
Is it possible? and if not, any ideas on how to do something similar?
An 'added' event is dispatched whenever a child display object is added to the display list via addChild() or addChildAt(). In the DisplayObjectContainer class add the listener:
addEventListener(Event.ADDED, onAdded);
and the handler:
private function onAdded(e:Event):void
{
trace('number of children is now ' + numChildren);
}
Using Event.ADDED_TO_STAGE on stage Object and setting useCapture to true.
More info on event here
Example:
function onAdded(e:Event):void{
trace(e.target.toString()); //use target to get the Object added
}
stage.addEventListener(Event.ADDED_TO_STAGE, onAdded, true); // set capture to true
I don't know if there is a built in way to do this.
Alternatives include the obvious,
private var _num_children:Number = 0;
addEventListener(Event.ENTER_FRAME, _checkChildren, false, 0, true);
private function _checkChildren($evt:Event):void {
if (this.numChildren != _num_children) {
_num_children = this.numChildren;
// There was a child (or more) added in the last frame execution
}
}
However, this seems like a more elegant solution...
public function _addChild($do:DisplayObject) {
$do .addEventListener(Event.ADDED_TO_STAGE, _childAdded);
addChild($do );
}
private function _childAdded($evt:Event) {
// do whatever with $evt.target
}
The difference here, is the _childAdded will get fired for each and every child added via _addChild method. This means if you are doing some costly code execution you will be doing it once for each child instance.
If you use the first method, you are only calling the method once per frame, and if 10 images are added on a single frame, then it will only run once.