Removing Event Listener in Deactivate/Activate Event - AS3 - actionscript-3

I am using the code snippet from Flash Cs6 for my mobile Air app. How do I properly remove the listener? ( I get error 1120: Access of undefined property ocean_slider. ) with the following code. Thanks for your help.
/* Deactivate/Activate Event
Conserve CPU and battery life by suspending expensive processes, such as ENTER_FRAME and TIMER events, when the application is not in focus.
Instructions:
1. Start timers and add event listeners in "fl_Activate".
2. Stop timers and remove event listeners in "fl_Deactivate".
*/
stage.addEventListener(Event.ACTIVATE, fl_Activate);
stage.addEventListener(Event.DEACTIVATE, fl_Deactivate);
function fl_Activate(event:Event):void
{
// Start timers and add event listeners here.
naturepage.sliders.ocean_slider.addEventListener(Event.ENTER_FRAME, ocean_slider);
function ocean_slider(e:Event):void
{
ocean_transform.volume = (naturepage.sliders.ocean_slider.value/100);
ocean_channel.soundTransform = ocean_transform;
}
}
function fl_Deactivate(event:Event):void
{
// Stop timers and remove event listeners here.
naturepage.sliders.ocean_slider.removeEventListener(Event.ENTER_FRAME, ocean_slider);
}
OK. I just changed the code to the following but still have the problem. The listener is definitely getting added because the functions work in my app after publishing without the removeEventListener. But once I add the code to remove the listener, I get the error 1120 when I try to publish.
stage.addEventListener(Event.ACTIVATE, fl_Activate);
stage.addEventListener(Event.DEACTIVATE, fl_Deactivate);
function fl_Activate(event:Event):void
{
addEventListener(Event.ENTER_FRAME,myFunction);
function myFunction(event:Event):void
{
ocean_transform.volume = (naturepage.sliders.ocean_slider.value/100);
ocean_channel.soundTransform = ocean_transform;
}
}
function fl_Deactivate(event:Event):void
{
removeEventListener(Event.ENTER_FRAME,myFunction);
}

It looks like your brackets are off - is that the code you copied and pasted or what you're trying to run. I'll edit the code after you confirm why the brackets are the way they are - specifically the 2 '}' in the ocean_slider function
Regardless, it means that ocean_slider isn't available and hasn't been added to the stage. So if it has, you need to check to make sure you're not calling fl_Deactivate before things are ready. And you also have a function call and stage item with the same name - ocean_slider - I'd change that and see if it works.

ok. I think it's working now. I put the function all the way outside. Thanks for your help and responses.
function myFunction(event:Event):void
{
ocean_transform.volume = (naturepage.sliders.ocean_slider.value/100);
ocean_channel.soundTransform = ocean_transform;
}
stage.addEventListener(Event.ACTIVATE, fl_Activate);
stage.addEventListener(Event.DEACTIVATE, fl_Deactivate);
function fl_Activate(event:Event):void
{
addEventListener(Event.ENTER_FRAME,myFunction);
}
function fl_Deactivate(event:Event):void
{
removeEventListener(Event.ENTER_FRAME,myFunction);
}

Related

Actions Script 3 Listener Without Function

Question:
Is there a way to use a on-click listener without calling a function? I only started actions script yesterday so please excuse if my question does seem naive.
Reason For Question:
I am hoping to break out of a while-loop when MouseEvent.OnClick happens. The problem is when using a function in the Listener function it only breaks out of the function defined for itself and not the while-loop I am stuck in.
function PlayGame():Void {
while (true) {
OnAndOff.addEventListener(MouseEvent.CLICK):void{ //Removed the ,function part
PowerButton.Switch();
break;
}
}
}
It's not a best way is to use infinite loop in as3, cause as3 has single thread. It mean that application will freezed when you will start your infinite loop. AS3 has event-based architecture so any user interaction dispatches some events.
If you need to call PowerButton.Switch() when user clicks on OnAndOff just call it in event handler:
OnAndOff.addEventListener(MouseEvent.CLICK, onOnAndOffClick); // somewhere on initialize
function onOnAndOffClick(event:MouseEvent):void {
PowerButton.Switch();
}
If you need to use approach similar to infinite loop you can use ENTER_FRAME event:
stage.addEventListener(Event.ENTER_FRAME, someFunction);
function someFunction(event:Event):void { // this function will be called automatically on each frame
// some code...
}

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
}

actionscript 3.0 function mouseevent event handler

I have a function that uses a mouse event and it removes and adds things onto the stage:
beginBut.addEventListener(MouseEvent.CLICK, bgnListener);
function bgnListener (event:MouseEvent) {
removeEventListener(Event.ENTER_FRAME, setScreen);
removeChild(beginBut);
removeChild(myWord);
healthBar.addEventListener(Event.ENTER_FRAME, healthLose);
ball.addEventListener(Event.ENTER_FRAME, moveBall);
myGem.addEventListener(Event.ENTER_FRAME, addGem);
myScore.addEventListener(Event.ENTER_FRAME, scoreCount);
healthBar.width+=1000;
}
However after some other things happen, I need this event to occur again. I have already
added beginBut but when I use
beginBut.addEventListener(MouseEvent.CLICK, bgnListener);
the event adds and removes the things automatically when the function that adds beginBut back occurs and not when I actually click on beginBut. I have also tried
bgnListener();
but it says that there is the wrong number of arguments. I already searched all over and I can't seem to fix this. Any help would be greatly appreciated.
If you call bgnListener() like you are now, you'll get an argument mismatch error because the function is expecting to receive a MouseEvent.
If you want to be able to call bgnListener() on its own like that, you can define a default value for your argument event, which can be null:
function bgnListener(event:MouseEvent = null)
{
// ...
}

Parameter child must be non-null error in AS3

I have a code to pause the game, and it runs the pause function as shown:
public function onKeyPress(keyboardEvent:KeyboardEvent) :void
{
//Check for pause
if(keyboardEvent.keyCode == Keyboard.P)
{
//If the timer is still running
if(gameTimer.running)
{
gameTimer.stop();
Mouse.show();
pauseText = new PauseText();
pauseText.x = 150;
pauseText.y = 100;
addChild(pauseText);
//If the player is using the mouse, resume by clicking on the player
if(mouseControl)
{
player.addEventListener(MouseEvent.CLICK, resumeGame);
pauseText.pauseInformation.text = "click on yourself";
}
else
{
pauseText.pauseInformation.text = "press 'p'";
}
}
else
{
//Only allow the player to resume with P IF he is using the keyboard
//This prevents cheating with the mouse.
if(!mouseControl)
{
gameTimer.start();
removeChild(pauseText);
pauseText = null;
}
}
}
}
The game runs perfectly fine. On my first playthrough, the pause functions work. However, if later I die and restart the game, then pause it, I get the following message:
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at Game/onKeyPress()
The game still runs fine though. However, everytime I pause, or unpause, this error appears. If I die again, restart, then pause, TWO of these errors appears. From what I can gather it seems as if it attempts to remove the pauseText…but I’ve been removing it just fine on the first playthrough, I’ve used removeChild() then set as null for other parts of my code and it works fine. Additionally, if I add a trace(“a”); statement right after the function header, I get the error before the “a” appears on the output panel.
What’s wrong?
Additional notes:
If I don’t use the pause function at all for my first playthough, there is no error when I call it up on my second playthrough.
put removeChild into 'if' ,this will solve error :
if(pauseText.parent){
pauseText.parent.removeChild(pauseText);
}
but You should anyway check what is the source of problem , maybe 'gameTimer.running' is false on beggining ?
You probably instantiate another Game object (the one that contains the whole game) while not removing the previous game's event listener. That would explain such behavior, since you have more than one KeyboardEvent.KEY_DOWN listener active, and note that when you're stopping the game, you most likely stop the timer in it, so the "else" clause of your "if (gameTimer.running)" statement is executed, but the timer was effectively stop without pauseText to be generated. So, you miss a
removeEventListener(KeyboardEvent.KEY_DOWN,onKeyPress);
in your game destruction code.
if (!mouseControl) {
gameTimer.start();
if (pauseText && contains(pauseText)) {
removeChild(pauseText);
pauseText = null;
}
}

Program Flow and Event Listeners in ActionScript

I'm having difficulty with program flow control when XML loads from an external source. I'm using Flash Builder 4.6
What should happen: use a loader. Listen for when it is loaded. When it fires the COMPLETE event, proceed.
What is happening: use a loader. Listen for when it is loaded. But before the COMPLETE event fires, program control returns to the calling class. So meanwhile, the program keeps going and throws an error because it is trying to access data which is null.
Three classes are involved:
AdvocacyWiz.mxml - the base MXML file for this AIR mobile app
Model.as - the model class
myXMLLoader.as - class in which the loader loads data, then
dispatches event when it's done.
I am calling a function setUpModel() in AdvocacyWiz.mxml once AdvocacyWiz.mxml is added to the stage. The setUpModel function in that class just looks like this:
Model.Instance.initialize(); //model is a singleton
The initialize function call invokes a loader in my XMLLoader.as class. That's where the event listener is left listening for the Complete event, which happens too late to prevent a null error from occurring.
EDIT: Here's the code --
In AdvocacyWiz.mxml (this fires first):
protected function addedToStageHandler(event:Event):void
{
setUpModel();
stage.scaleMode = StageScaleMode.NO_SCALE;
//... additional layout functions
private function setUpModel():void {
Model.Instance.initialize();
}
In Model (this fires second):
public function initialize():void {
addEventListeners(); //this includes listener for StoriesXMLLoader.STORY_LOADED event which will be dispatched from the loader class.
useExternalXML();
...
}
private function useExternalXML():void
{
myLoader.getStory("1140");
}
In the Loader class:
public function getStory(storyId:String):void {
var url:String = "http://mysite.com/whatever.xml";
myLoader.dataFormat = URLLoaderDataFormat.TEXT;
myLoader.addEventListener(Event.COMPLETE, storyXMLLoaded);
myLoader.load(new URLRequest(url));
}
private function storyXMLLoaded(e:Event):void {
storyXML = new XML(e.target.data);
dispatchEvent(new Event(StoriesXMLLoader.STORY_LOADED));
}
What I want to do is call setUpModel() but not have flow returned to the mxml class until the model actually has data.
Thanks.
Well, you haven't provided any code, which would help considerably, but here's a few pointers.
What should happen: use a loader. Listen for when it is loaded. When
it fires the COMPLETE event, proceed. What is happening: use a loader.
Listen for when it is loaded. But before the COMPLETE event fires,
program control returns to the calling class. So meanwhile, the
program keeps going and throws an error because it is trying to access
data which is null.
A lot of Flex is asynchronous. What this means is that when you try to load from the external source, program control immediately returns, as you noted.
In order to have the kind of program flow you want, you need to have the function that calls load() end after that call. Then, when the COMPLETE event listener fires, you can execute the remainder of the code that causes the null exception.