Creating your own ADDED_TO_STAGE event - actionscript-3

Is it possible to create your own ADDED_TO_STAGE event?
I´m trying to pass some arguments to its handler...
It would be like this:
addEventListener(Event.ADDED_TO_STAGE, arg1, arg2, init)
There´s any workaround for this?
Thanks.

Visiting this link will provide an in-depth answer on this, however here's a quick and dirty snapshot:
A function called by a listener can only have one argument, which is the event triggering it.
You will need to either call another function from your listener function, or create a custom event to hold the properties you want to parse. The latter is recommended, but here's how you could implement the former:
function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
finalize(arg1, arg2);
}
function finalize(a:*, b:*):void
{
trace(a, b);
}

Related

"Warning: 1090: Migration issue" despite explicitly registering event handlers

I have a game engine class that inherits from MovieClip and handles mouseDown events in a private instance method. For the sake of simplicity, I name the event handler onMouseDown. It looks like this:
private function onMouseDown(e:MouseEvent):void
{
if (_isEnginePlaying)
{
_player.attack();
}
}
I register it in the engine class's init method (itself an addedToStage handler) that looks like this:
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// ...
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
// ...
}
This compiles and works correctly, but the compiler warns:
Warning: 1090: Migration issue: The onMouseDown event handler is not triggered automatically by Flash Player at run time in ActionScript 3.0. You must first register this handler for the event using addEventListener ( 'mouseDown', callback_handler).
But, as shown, I did register the handler using addEventListener(). Why does the compiler still emit this warning and what can I do to make the warning go away?
This is because the handler was registered with a different instance and not the game engine instance (this). Remember that event handlers in AS2 were registered simply by specifying them as properties on the instances that should handle the respective events and will automatically fire when needed. The warning is there to inform the developer that they do not automatically fire in AS3.
So the compiler errs on the side of caution, assumes I'm trying to register a click handler with the game engine instance (even though I've already registered it with the stage) and warns me to that effect.
There are a number of ways to make the warning go away:
Just rename the onMouseDown handler. The "on-" convention is a holdover from AS2 and the compiler only emits warnings for handlers following this naming convention; if you're comfortable settling for a different convention, this is the recommended solution. The convention used at Adobe is "-Handler" (sources 1 2 3), so onMouseDown becomes mouseDownHandler:
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// ...
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
// ...
}
private function mouseDownHandler(e:MouseEvent):void
{
if (_isEnginePlaying)
{
_player.attack();
}
}
If this can listen for the event, register the event with this instead:
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// ...
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
// ...
}
If the intention was for this to listen for the event, then registering the handler with a different instance was inappropriate.
If you need to register the handler with a different instance, but you're sure that the definition is where it should be, you'll have to rename the handler. If for whatever reason changing the naming convention across all of your event handlers is not an option, the least you should do is prefix the name of whatever you're registering it with, so it's clear that this handler is not intended to be registered with this, but with a different instance:
private function stage_onMouseDown(e:MouseEvent):void
{
if (_isEnginePlaying)
{
_player.attack();
}
}
If you insist on keeping things the way they are, you can suppress the warning altogether, but obviously this is not something you should be doing unless you have a very good reason to.

in AS3, removeEventListener(Event.ENTER_FRAME) is not working

I have been dealing with this problem for days already. I am at my wits' end!
I can't seem to find a definitive answer anywhere on any of the forums, documentation, etc.
Everything looks fine at first run, or when I load a next level for the user to play. But if the user hits the ESC key to load a different level, the ENTER FRAME listener does not get removed and it duplicates all the triggers in it, showing the player going really fast, and all funky, because it builds on top of the previously instantiated ENTER FRAME listener.
I don't know if I have a problem of an anonymous function, or an unknown instance being referenced in my removeEvent... command... Bottom line, I give up and I need this working HELP!!!
Here's the code:
function initPlay():void
{
//code here determining what display object to add to the list and assign it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel){
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
removeChild(currentLevel);
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME)); //outputs TRUE if called from loadNextLevel but FALSE if called from initPlay() !!!
}
}
I also tried to add and remove the eventListener to stage, MovieClip(Root), or nothing at all and the result is always the same.
I know that there may be other ways to design such a process, but please note I am not really flexible at the moment on doing this because the project is very long (about 4000 lines of code) and removing the ENTER FRAME this way, crazy or not should still work!!
THANK YOU in advance for anyone willing to help.
The problem appears to be the nested functions inside the initPlay() method.
Each time you call initPlay() you are defining new functions. Some of these nested functions call initPlay() themselves.
Functions are objects (memory references). So each time you call initPlay() you are making new references to new functions. So when you try to remove an event listener, you're only able to remove one of these event handlers (the one in the current scope of execution).
I'm not sure if I'm explaining this clearly, perhaps this example will help. I'll use numbers to represent the references to each function, and a simple scenario that is similar to yours:
function example():void
{
addEventListener(MouseEvent.CLICK, mouseClickHandler);
function mouseClickHandler(event:Event):void
{
if (someCondition)
{
example();
}
else
{
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
}
}
When we run this function the first time, a new function is defined within the scope of the example() function. Lets use the number 1 to represent the reference to this nested function. someCondition is true on the first time around, and so the example() function is called again.
On the second execution of the example() function, a new reference to the mouse event handler is created (#2). We also add the event listener again. At this point, there are two event handling functions in memory, and both will be executed when the event is dispatched.
Let's say that in the second invocation of example() that someCondition is false and now we want to remove the listener. When we call:
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
It's referring to event handler #2. Event handler #1 still exists, and because it's hidden in the scope of the first invocation of example() it can't be removed here.
My simple example breaks down after this... but I hope it makes it clear why your event handlers shouldn't be nested inside a function. Admittedly, this is difficult to describe and even more so in a real world example like yours. But I'm pretty confident that this is the source of most, if not all, of the issues you describe.
Here's how I was able to get around this without changing the scope of the nested functions (although I agree that would be the preferred solution) by creating a boolean variable called "loadingNewGame" and changing it to true from outside the onEnterFrame (in fact, this assignment was done from initPlay() and then from onEnterframe I called removeEnterFrameListener() function. This did the trick.
here's the code in case anybody is interested:
// package, and other code here.
var loadingNewGame:Boolean = new Boolean(false);
function initPlay():void
{
//code here determining what display object to add to the list and assign
//it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel)
{
loadingNewGame = true;
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
loadingNewGame:Boolean = false;
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
if(loadingNewGame)
removeChild(currentLevel);
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME));
//outputs true
}

Add EventListener to function?

Quick question... Is is possible to attach an EventListener to a function? Such that if at any point in a function's execution an Event is Dispatched the EventHandler will get fired?
Cheers.
You can use the stage to dispatch such an event and listen to it:
stage.addEventListener("myFunctionWasCalled", callback);
myFunction();
public function callback(event:Event):void {
trace("callback executed");
}
public function myFunction():void {
stage.dispatchEvent(new Event("myFunctionWasCalled"));
}
Event listeners are attached to objects that belong to a class that descends from EventDispatcher. You cannot attach them to a function.
You can achieve this by simply passing a function reference (callback) as an argument
function cb(s:String):void {
trace(s);
}
function doit(f:Function):void {
// do something
f("Hi");
// do some more stuff
}
doit(cb);
If you only want to listen for a event in the lifetime of a function call just add/remove as needed.
example:
function somefunction():void{
someobject.addEventListener(Event, eventhandler);
... doing stuff
someobject.removeEventListener(Event, eventhandler);
}
Now keep in mind if your doing this then your choice in even flows may become very hard to track down the road.
Typicality you only really need to worry about this in the life and death of a object vs the life and death of a function call.
To attach an event listener to an object this object must implement IEventDispatcher interface. So you could extend Function class and add your methods. AS3 docs say that Function is not final, but AS3 compile disagrees with it: VerifyError: Error #1103: Class Bla cannot extend final base class.
So, the short answer is: you can't attach an event listener to a function.
But as it was already mentioned you could:
Pass callback/callbacks to the function which will be called at some point: function bla(callback1:Function, callback2:Function):void.
Dispatch events from some other object during function execution, for example you could make a Functor class which has method execute() and implements IEventDispatcher. In this way you'd call the function myFunctor.execute() and could get dispatched events.

Actionscript MouseEvent

I have a MouseEvent function and I need to call it without MouseEvent as well. I know there is quick and easy way to do it but I forgot....
my_mc.addEventListener(MouseEvent.CLICK, callEvent);
function callEvent(e:MouseEvent):void
{
trace("Mouse event called");
}
callEvent()???
now I need to call the same event without any events. I know I can create a new function without any event an call that from callEvent. But that is not what I am looking for...
Thanks,
Rex
You can do that easily like this (put "= null" after the event arg):
my_mc.addEventListener(MouseEvent.CLICK, callEvent);
function callEvent(e:MouseEvent = null):void
{
trace("Mouse event called");
}
When you call the function simply insert null in the braces
Like that-
CallEvent(null);
from this:
dispatchEvent(new CustomEvent(CustomEvent.ONLOADED, data ));

Clearing eventListeners on a FileReference object

I have a strange issue! I am trying to remove an event listener on a FileReference object by calling a function, but it seems not to be removed, and I do not understand why.
Here is the code:
private function clearFileUploadListeners(file:FileReference, index:String):void {
var dispatchEvent:Function = function(event:Event):void {
dispatch(event.type, event, index);
};
file.removeEventListener(Event.COMPLETE, dispatchEvent);
var bool:Boolean = file.hasEventListener(Event.COMPLETE);
if (bool)
trace("ERROR");
}
When I run this code, the trace actually happens. I don't understand why this boolean returns true, when I just tried to remove the eventListener just above! I guess I am probably doing something really stupid because it seems like a strange error.
I hope someone can please help me on this issue.
EDIT:
I believe it has to do with the fact that the dispatchEvent function is defined inside another function when I add the listener:
private function upload(file:FileReference, index:String):void {
var dispatchEvent:Function = function(event:Event):void {
dispatch(event.type, event, index);
};
file.addEventListener(Event.COMPLETE, dispatchEvent);
}
The problem is that I need to access this "index" variable from the listener, and I can't set it as a global variable as each file has it's own index and it's a burden if I have to extend each event class to keep track of the index (Event, ProgressEvent, ..). I hope someone can please help me on this.
EDIT2:
I actually found a temporary solution, I am not sure if it is the best! I put my removeListener method actually inside the upload method, but made it a variable. As AS3 allows dynamic object, I attached this method to one of my object, and so I just call the reference to the method when necessary. The event is actually removed. Is this a good solution please?
Thank you very much,
Rudy
You're right, it has to do with the fact that you're defining a function inside another function, then using it to handle events.
Each time the function upload is called, it creates a new closure, and assigns a reference to it to the dispatchEvent variable, which is then passed to the addEventListener class. So each time upload is called, it is using a new, different closure in the call to addEventListener. Similarly, in the clearFileUploadListeners function, a new closure is being created on each call (which happens to have the same code each time, but isn't the same function object). The call to removeEventListener does nothing if the given callback has not been added as an event listener for the given event, which is the case here.
To solve your problem, you need to store a reference to the closure that you pass to the addEventListener function. This way, you can get a reference to the same closure that was added when you need to remove it later in clearFileUploadListeners.
You can try something along the lines of the following code (untested):
import flash.utils.Dictionary;
var callbackRegistry:* = new Dictionary();
private function upload(file:FileReference, index:String):void {
var dispatchEvent:Function = generateFileUploadCompleteCallback();
callbackRegistry[file] = dispatchEvent;
file.addEventListener(Event.COMPLETE, dispatchEvent);
}
private function clearFileUploadListeners(file:FileReference, index:String):void {
var dispatchEvent:Function = callbackRegistry[file];
callbackRegistry[file] = null;
file.removeEventListener(Event.COMPLETE, dispatchEvent);
var bool:Boolean = file.hasEventListener(Event.COMPLETE);
if (bool)
trace("ERROR");
else
trace("YAY, ALL OK!");
}
private function generateFileUploadCompleteCallback(index:String):Function {
return function(event:Event):void {
dispatch(event.type, event, index);
};
}
Two other things to note on this subject.
If you must utilize a native Event directly then you should pretty much always make sure and use these last three optional params :
myObject.addEventListener( Event.COMPLETE, myFunction, false, 0, true );
Check Grant Skinner's post on the subject here :
http://gskinner.com/blog/archives/2006/07/as3_weakly_refe.html
And the very best practice of all is to ALWAYS (seriously always) use Robert Penner's Signals (instead of custom events) and his NativeSignals (to wrap needed native Flash events).
Five times faster than Flash's native events.
Always safe with weak references.
Any number of typed payload(s) in each Signal.
Get the SWC here :
https://github.com/robertpenner/as3-signals
Signals were designed to solve the very problem you are having.
Imagine instead of creating an array and managing that to remove all listeners if you could just call :
signalBtnClicked.removeAll();
or
signalBtnClicked.addOnce( function( e : MouseEvent ) : void { /* do stuff */ } );
Knowing that the closure you just created will immediately be dereferenced once it is called and happily go night night when the GC makes its rounds.