fmchan
fmchan

Reputation: 760

How to call removeEventListener when using addEventListener with parameters passing in AS3

I am trying to make a object, which is a movieclip, move up and down continuously and print its y-axis value whenever it finished a move. The below code works fine.

var tweenUp:Tween = null, tweenDown:Tween = null;

function up():void {
    tweenUp = new Tween(person,"y",None.easeNone,person.y,person.y+20,1,true);
    tweenUp.addEventListener(TweenEvent.MOTION_FINISH, finishedUp);
}
function down():void {
    tweenDown = new Tween(person,"y",None.easeNone,person.y,person.y-20,1,true);
    tweenDown.addEventListener(TweenEvent.MOTION_FINISH, finishedDown);
}

function finishedUp(event:TweenEvent):void {
    trace(person.y);
    tweenUp.removeEventListener(TweenEvent.MOTION_FINISH, finishedUp);
    tweenUp = null;
    down();
}
function finishedDown(event:TweenEvent):void {
    trace(person.y);
    tweenDown.removeEventListener(TweenEvent.MOTION_FINISH, finishedDown);
    tweenDown = null;
    up();
}

up();

However, I am looking for a solution to pass a object to the callback function of listener. I try to use the way shown below but it does not work.

var tweenUp:Tween = null, tweenDown:Tween = null;
var functionFinishedUp:Function = null, functionFinishedDown:Function = null;

function up(object:MovieClip):void {
    tweenUp = new Tween(object,"y",None.easeNone,object.y,object.y+20,1,true);
    functionFinishedUp = finishedUp(object);
    tweenUp.addEventListener(TweenEvent.MOTION_FINISH, functionFinishedUp);
    tweenUp.removeEventListener(TweenEvent.MOTION_FINISH, functionFinishedUp);
    tweenUp = null;
}
function down(object:MovieClip):void {
    tweenDown = new Tween(object,"y",None.easeNone,object.y,object.y-20,1,true);
    functionFinishedDown = finishedDown(object);
    tweenDown.addEventListener(TweenEvent.MOTION_FINISH, functionFinishedDown);
    tweenDown.removeEventListener(TweenEvent.MOTION_FINISH, functionFinishedDown);
    tweenDown = null;
}

function finishedUp(object:MovieClip):Function {
    return function(event:TweenEvent):void {
        trace(object.y);
        down(object);
    }
}
function finishedDown(object:MovieClip):Function {
    return function(event:TweenEvent):void {
        trace(object.y);
        up(object);
    }
}

up(person);

It just goes up and then do nothing because it seems that the listener is removed just after added. Is there any good solution to remove the listener which has parameters passing, after the listener finished its task?

Thanks in advance for any help you are kind enough to provide!

Upvotes: 1

Views: 2310

Answers (2)

If you're looking for an easy way to remove an event listener right after the event was received, you can do this:

function listener(event:Event):void
{
     // stop listening to the dispatcher for this event type
     EventDispatcher(event.target).removeEventListener(event.type, arguments.callee);
     // ...and do whatever else you need to do here
}

That line can be used in any event listener.

Another option would be to use Signals (https://github.com/robertpenner/as3-signals). They have an addOnce method that will only listen once and then remove themselves.

Upvotes: 3

Joshua Honig
Joshua Honig

Reputation: 13215

First, I advise against using object as the name of a variable! Although technically allowed it is semantically meaningless and unhelpfully similar to the reserved class name Object.

The reason your code isn't working is that you are removing the event handler immediately after adding -- before it ever has a chance to get invoked. You really don't ever have to remove the listeners, because you want them to be invoked every time the respective tweens are complete. If you insist on attaching and detaching the listener functions every iteration you'll have to remove the event listener in the listener function itself. Of course this gets rather tricky with all those closures you're creating and tossing.

In fact you don't need the closures at all, since the tween objects have a reference to the tweened object in the obj property. In addition, you only need to create one up tween and one down tween for each target person, and can then just rewind() and start() each tween back and forth. You can keep track of which tween goes with which person using a couple of Dictionary objects, which use object references as the keys.

Here's a proof of concept that generates twenty persons all with their own set of tweens -- but only two handlers are defined and neither of them are closures. I've also combined up with finishedDown and down with finishedUp:

var upTweens:Dictionary = new Dictionary();
var downTweens:Dictionary = new Dictionary();

for(var i:uint = 0; i < 20; i++) {
    // Make a new Person
    var person:Person = new Person();
    person.y = 100;
    person.x = i * 20;
    addChild(person);

    // Create the tweens but stop them before they can play at all
    var top = person.y - 20;   // Up is -y in Flash
    var bottom = person.y;
    var upTween:Tween = new Tween(person, "y", None.easeNone, bottom, top, 1, true);
    upTween.stop();
    upTween.addEventListener(TweenEvent.MOTION_FINISH, onUpFinished);
    var downTween:Tween = new Tween(person, "y", None.easeNone, top, bottom, 1, true);
    downTween.stop();
    downTween.addEventListener(TweenEvent.MOTION_FINISH, onDownFinished);

    // Associate tweens with this person, and start it up!
    upTweens[person] = upTween;
    downTweens[person] = downTween;
    upTween.start();
}


function onUpFinished(e:TweenEvent):void {
    var upTween:Tween = Tween(e.currentTarget);
    var person:Person = Person(upTween.obj);
    var downTween:Tween = Tween(downTweens[person]);
    downTween.rewind();
    downTween.start();
}

function onDownFinished(e:TweenEvent):void {
    var downTween:Tween = Tween(e.currentTarget);
    var person:Person = Person(downTween.obj);
    var upTween:Tween = Tween(upTweens[person]);
    upTween.rewind();
    upTween.start();
}

Upvotes: 1

Related Questions