Passing arguments into mouseEvent function - actionscript-3

This seems simple in other languages, but I don't understand the error. I have 7 buttons that I would like to each take my gallery movieclip to a certain frame when you click on them.
Error: 1067: Implicit coercion of a value of type int to an unrelated type flash.events:MouseEvent.
Error: 1136: Incorrect number of arguments. Expected 2.
Error: 1067: Implicit coercion of a value of type void to an unrelated type Function.
Any help?
function gotoImage(event:MouseEvent, frameParam:int):void
{
MovieClip(this.root).gallery.gotoAndStop(frameParam);
}
t1.addEventListener(MouseEvent.CLICK, gotoImage(1));
t2.addEventListener(MouseEvent.CLICK, gotoImage(2));
t3.addEventListener(MouseEvent.CLICK, gotoImage(3));
t4.addEventListener(MouseEvent.CLICK, gotoImage(4));
t5.addEventListener(MouseEvent.CLICK, gotoImage(5));
t6.addEventListener(MouseEvent.CLICK, gotoImage(6));
t7.addEventListener(MouseEvent.CLICK, gotoImage(7));

You've got two things off with your code:
First, in ActionScript, event handlers always have the same signature:
function someHandler(e:Event):void { .. }
Sometimes the Event argument is a more specific subclass of Event, such as MouseEvent, but there is always just one argument.
The addEventListener method needs a function itself, not the result of invoking a function.
// Here's a function:
function multiply(i1:int, i2:int):int { return i1 * i2; }
// Here's assigning the result of **invoking** a function:
var result:int = multiply(2,3);
// Here's assigning a **function itself** to a variable:
var f:Function = multiply;
// You can invoke the function via the variable f in two different ways:
var result1 = f(2,3);
var result2 = f.apply(null, [2,3]);
So, you'll need to change your code to follow the above to points. You'll have to associate the buttons with jumping to a specific frame one of two ways:
Simple but repetitive: Use a separate handler for each button, with the frame hard coded into each handler.
1a. Named functions (most verbose):
function onT1Click(e:MouseEvent):void {
MovieClip(this.root).gallery.gotoAndStop(1);
}
t1.addEventListener(MouseEvent.CLICK, onT1Click);
// etc. etc.
1b. Anonymous functions:
t1.addEventListener(MouseEvent.CLICK, function(e:Event):void {
MovieClip(this.root).gallery.gotoAndStop(1);
});
// etc. etc.
More elegant: Use the same handler, and store the association between button and frame elsewhere, such as in a Dictionary. If you stick with your naming convention you could even fill the Dictionary in a for loop getting the buttons by name:
var buttonToFrame:Dictionary = new Dictionary();
for(var i:int = 1; i < 8; i++) {
var btn:Button = this["t" + i.toString()];
buttonToFrame[btn] = i;
btn.addEventListener(MouseEvent.CLICK, onClick);
}
function onClick(e:MouseEvent):void {
var btn:Button = Button(e.currentTarget);
var frameNum:int = buttonToFrame[btn];
MovieClip(this.root).gallery.gotoAndStop(frameNum);
}

Just change this
t1.addEventListener(MouseEvent.CLICK, function(me:MouseEvent):void{ gotoImage(me, 1)});
t2.addEventListener(MouseEvent.CLICK, function(me:MouseEvent):void{ gotoImage(me, 2)});
and so on...

This is possible with a roundabout approach. For the event handler, use a function that returns a nested anonymous function.
private var textFieldA:TextField = new TextField;
private var textFieldB:TextField = new TextField;
public function setParameterizedTextWhenTextFieldsAreClicked ():void {
addChild(textFieldA);
textFieldA.text = 'Text field A';
textFieldA.addEventListener(MouseEvent.CLICK, showCustomMessage("One"));
addChild(textFieldB);
textFieldB.text = 'Text field B';
textFieldB.y = 20;
textFieldB.addEventListener(MouseEvent.CLICK, showCustomMessage("Two"));
// NOTE: We must use strongly referenced listeners because weakly referenced
// listeners **will get garbage collected** because we're returning
// an anonymous function, which gets defined in the global namespace and
// thus, the garbage collector does not have anything pointing to it.
}
private function showCustomMessage (message:String):Function {
// NOTE: You can store the following function to a class variable
// to keep it in memory, which would let you use weakly referenced
// listeners when using this as an event handler. Many people
// would find that awkward. I would discourage that.
return function (e:MouseEvent):void {
var textField:TextField = e.target as TextField;
textField.text = message; // "message" argument is available because
// this function's scope is kept in memory.
}
}
Bear in mind, the use of anonymous functions and reliance on function scope being kept in memory seem to present garbage collection complications.

Related

JSON.parse(...).reelarray is null - ActionScript

in return i am getting null value
public function spin()
{
var decoded : Object;
trace("i am innnnnnn");
var sendLoaders:URLLoader;
var sendRequests:URLRequest;
sendLoaders = new URLLoader();
sendRequests = new URLRequest("http://localhost/getspinvalue/4");
var difvar:Array = [];
sendLoaders.addEventListener(Event.COMPLETE, botCompleted);
sendLoaders.load(sendRequests);
function botCompleted(e:*)
{
decoded = JSON.parse(e.currentTarget.data);
}
trace(decoded);
return decoded.reelarray;
}
well, the sendLoaders.load() is an asynchronous event, your function returns null since the botCompleted() function hasnt fired when the function returns
You need to continue the execution inside you botComplete function (if you need to do different things after each spin() you can pass a function that gets executed)
public function spin() {
var decoded : Object;
trace("i am innnnnnn");
var sendLoaders:URLLoader;
var sendRequests:URLRequest;
sendLoaders = new URLLoader();
sendRequests = new URLRequest("http://localhost/getspinvalue/4");
var difvar:Array = [];
sendLoaders.addEventListener(Event.COMPLETE, botCompleted);
sendLoaders.load(sendRequests);
trace("request sent")
}
function botCompleted(e:*) {
decoded = JSON.parse(e.currentTarget.data);
trace("data recieved:" + decoded)
//CONTINUE EXECUTION HERE
}
URLLoader.load is an asynchronous method, it means that it spawns a process to load URL, and the execution of spin continues. So it returns a value at once. The botCompleted function is executed when the request is fulfilled, later. Something like that:
sendLoaders.load()
return value
botCompleted()
So you should rely on the botCompleted function, rather than on the return value. All the logic that depends on the decoded result should be triggered inside the botCompleted function.
function botCompleted(e:Event):void {
decoded = JSON.parse(e.currentTarget.data);
handleDecodedResult(decoded);
}
i want my spin method to return the decoded value .so how can i change
my code.
This is not possible. The JSON data is loaded asynchronously, but the function returns synchronously. In other words, the spin() function returns immediately (like all functions do), but the URLLoader doesn't complete until later in time. It doesn't matter that the botCompleted handler is written before your return statement, it won't get called until later. This is what event handlers are for: to handle asynchronous events.
What you can do is pass in callback functions. For example:
function spin(callback:Function):void {
var loader:URLLoader = new URLLoader();
// .. load
loader.addEventListener(Event.COMPLETE, complete);
function complete(e:Event):void {
var decoded:Object = JSON.parse(e.target.data);
callback(decoded);
}
}
Now you can call your spin function and pass an anonymous function in the context that you call spin, which almost makes it look synchronous:
spin(function(data:Object):void {
trace(data);
// Do stuff with data here
});
// Note: data is not available yet from here, only from inside the callback above
This is a common practice (in both AS3 and JS), though not without its pitfalls.

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!
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.
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();
}

Timer in Action Script 3

I have problem that I need to create timer but I want to pass on a variable to it, how to do it? Is it possible in AS3?
I tried like this:
bonusPlayer1Timer = new Timer(5000);
bonusPlayer1Timer.addEventListener(TimerEvent.TIMER, bonusChanges(player1));
bonusPlayer1Timer.addEventListener(TimerEvent.TIMER_COMPLETE, bonusChangesRemove(player1));
bonusPlayer1Timer.start();
function bonusChanges(event:TimerEvent, playerBonus:Player):void {
switch (playerBonus.bonus) {
case 0 :
playerBonus.multipleShooting = false;
playerBonus.bonus = -1;
break;
...}}
But I have error:
1067: Implicit coercion of a value of type Player to an unrelated type flash.events:TimerEvent.
1136: Incorrect number of arguments. Expected 2.
And this error is in the bold line.
Can I use it in this way? Or I have to create two the same function for every of my players because I am not allow to pass on any different arguments to the timer function?
Thank you,
Create a class that extends the Timer class and add a property for the Player.
public class PlayerTimer extends Timer
{
public var thePlayer:Player;
public function PlayerTimer(delay:Number, repeatCount:int=0)
{
super(delay, repeatCount);
}
}
Using your example the code would be something like this:
bonusPlayer1Timer = new PlayerTimer(5000);
bonusPlayer1Timer.thePlayer = new Player();
bonusPlayer1Timer.addEventListener(TimerEvent.TIMER, bonusChanges);
bonusPlayer1Timer.addEventListener(TimerEvent.TIMER_COMPLETE, bonusChangesRemove);
bonusPlayer1Timer.start();
function bonusChanges(event:TimerEvent):void {
var playerBonus:Player = PlayerTimer(event.target).thePlayer;
switch (playerBonus.bonus) {
case 0 :
playerBonus.multipleShooting = false;
playerBonus.bonus = -1;
break;
...}}
You can never pass more than one argument into the function triggered by an EventListener. You need to find other ways of passing your information around, such as the solution provided by Nathan Smith.
you can also do it this way:
var x = setTimeout(yourfunction(/*arguments you need*/),1000);
function yourfunction(/*var*/){
//your code
}
Just Type
var bonusPlayer1Timer = new Timer(5000);

Creating anonymous functions in loop with not the same arguments

I want to make in loop set of buttons, and add to them some events, but anonymous functions is the same. I write example code:
for(var i:int=0;i<5;i++)
{
var button:SimpleButton = new SimpleButton(...);
...
button.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void
{
trace(i);
});
}
...
And I want to trace 0,1,2,3.. from click buttons instead of 4,4,4,4 ..
Do you know how can I make this ?
The problem you are running into is that ActionScript does not support closures.
In other words, the variable i does not get copied into it's own context per function. All functions refer to the same instance of i.
More information here:
http://flex.sys-con.com/node/309329
In order to do this, you need a function that generates a function:
public function makeFunction(i:int):Function {
return function(event:MouseEvent):void { trace(i); }
}
Now, you create new instances of the function with their own context:
button.addEventListener(MouseEvent.CLICK, makeFunction(i));

Dynamically Handling Events with Function Expression

I have a class which exposes literally dozens of events(before you get of on a tangent about whether that's good/bad design, just know that I didn't make that class). The event object of each event(eventParam in the below code) always has a toDebugString function, that basically creates a string containing all of the event object's property values:
propertyName1: propertyValue1
propertyName2: propertyValue2
propertyName3: propertyValue3
It works so far as creating all of the panels, with the title of each panel being the name of the event. However, the big problem is that all of events end up in the TextArea of the last panel. So there is something I don't understand about the anonymous method. It's as if each iteration of the loop uses the same function, and on the last iteration of the loop it decides that the debugPanel that was just created will be the one that all instances of that function will reference. In other words, a new unique debugSubPanel and TextArea is created in each iteration of the loop, but there is only one debugResponseListener event handler shared by all iterations of the loop. So my question is, how can I dynamically create the event handler function dynamically so that it stays associated with the debugSubPanel that I want it to?
public function debugPanelCreated(event:FlexEvent)
{
//iterate through all of the events exposed by mClient.ResponsesDispatcher
//where key is the name of the event
for (var key:String in mClient.ResponsesDispatcher.respMap)
{
//for each event, create a panel containing a text box
var debugSubPanel:Panel = new Panel();
debugSubPanel.title = debugSubPanel.label = key;
var debugSubPanelTextArea:TextArea = new TextArea();
debugSubPanel.addChild(debugSubPanelTextArea);
var debugResponseListener:Function =
function (eventParam :Object) : void
{
//use debugString function to write the properties
//of eventParam to the text box
debugSubPanelTextArea.text = eventParam .toDebugString();
};
//listen to this event:
mClient.ResponsesDispatcher.addEventListener(key,debugResponseListener);
//add the panel for this event
debugPanel.addChild(debugSubPanel);
}
}
Actionscript includes a feature called closures, which means that when you create an inner function and call it, the variables of its parent function are still available. (This is how debugResponseListener = function() ... works at all.) The issue is that a closure is only created when that function is called, and it uses the variable values from their last setting.
You can get around this by making a function that returns the listener function you want.
function makePanelListener(debugSubPanelTextArea:TextArea) : Function
{
return function(eventParam :Object) : void {
//use debugString function to write the properties
//of eventParam to the text box
debugSubPanelTextArea.text = eventParam .toDebugString();
}
}
and in your original code:
var debugResponseListener:Function = makePanelListener(debugSubPanelTextArea);
(There's a little explanation of what's going on in Explaining JavaScript scope and closures, look for the section called "The Infamous Loop Problem". More on closures at jibbering.)
This is the hack I came up with. I really don't like it, but it'll work for now. Open to suggestions still.
public class ResponseDispatcherToDebugStringHelper
{
public var textArea:TextArea;
public function responseToDebugStringHandler(eventParam:Object) : void
{
//use debugString function to write the properties
//of eventParam to the text box
textArea.text = eventParam.toDebugString();
}
}
public function debugPanelCreated(event:FlexEvent)
{
//iterate through all of the events exposed by mClient.ResponsesDispatcher
//where key is the name of the event
for (var key:String in mClient.ResponsesDispatcher.respMap)
{
//for each event, create a panel containing a text box
var debugSubPanel:Panel = new Panel();
debugSubPanel.title = debugSubPanel.label = key;
var debugSubPanelTextArea:TextArea = new TextArea();
debugSubPanel.addChild(debugSubPanelTextArea);
var helper:ResponseDispatcherToDebugStringHelper =
new ResponseDispatcherToDebugStringHelper();
helper.textArea = debugSubPanelTextArea;
//listen to this event:
mClient.ResponsesDispatcher.addEventListener(key,helper.responseToDebugStringHandler);
//add the panel for this event
debugPanel.addChild(debugSubPanel);
}
}