How to stop on Enter Frame firing multiple times? - actionscript-3

I'm trying to do a simple url requestion on enter frame, but it seems to be executing multiple times?
I tried removing the event handler on handleLoadSuccessful, that seems to stop it firing infinitely, but its still executing about 10 times before it stops.
How do I make it only fire once?
addEventListener(Event.ENTER_FRAME, onLoadHandler);
function onLoadHandler(event:Event) {
var scriptRequest:URLRequest = new URLRequest("http://ad.doubleclick.net/clk;254580535;19110122;t?http://www.gamespot.com.au/Ads/gswide/hp/1x1.gif");
var scriptLoader:URLLoader = new URLLoader();
var scriptVars:URLVariables = new URLVariables();
scriptLoader.addEventListener(Event.COMPLETE, handleLoadSuccessful);
scriptLoader.addEventListener(IOErrorEvent.IO_ERROR, handleLoadError);
scriptRequest.method = URLRequestMethod.POST;
scriptRequest.data = scriptVars;
scriptLoader.load(scriptRequest);
function handleLoadSuccessful($evt:Event):void {
trace("Message2 sent.");
removeEventListener(Event.ENTER_FRAME, onLoadHandler);
}
function handleLoadError($evt:IOErrorEvent):void {
trace("Message1 failed.");
}
}

ENTER_FRAME is fired x times per second, where x is the document frame rate.
The code within your listening function leads me to thinking that you don't actually need the event listener or the listening function at all - if you just want what's within that function called a single time, move everything within it outside on its own.
You could also move it all into an init() function and then call that once.
Also, your functions handleLoadSuccessful() and handleLoadError() should not be defined within another function (in your case onLoadHandler()).

Related

Why is my action script event not firing?

Presently, I am attempting to add the ability to capture all the slides of a presentation to images and save them to disk. It works now where the first page is capture, then I want an async event to fire when the second page has loaded to capture that page, and so on. Here is where I have added an event listener, though I'm not sure if I should be using stage or this:
import flash.events.Event;
private var jpgEncoder:JPGEncoder;
// ...
private function init():void{
// ...
// Add async event to capture second page after loading
stage.loaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
// ...
}
private function onPrintButtonClicked():void {
// screen capture code
jpgEncoder = new JPGEncoder(90);
// Page 1 capture
bitmapData1 = new BitmapData(stage.width, stage.height);
bitmapData1.draw(stage, new Matrix());
// move to next page
var curPage:Page = PresentationModel.getInstance().getCurrentPage();
if (curPage != null) {
LOGGER.debug("Go to next page. Current page [{0}]", [curPage.id]);
pageCount++;
dispatchEvent(new GoToNextPageCommand(curPage.id));
} else {
LOGGER.debug("Go to next page. CanNOT find current page.");
}
}
private function onLoadComplete(e:Event)
{
// Get page 2 capture
bitmapData2 = new BitmapData(stage.width, stage.height);
bitmapData2.draw(stage, new Matrix());
// Copy two pages to one bitmap
var rect1:Rectangle = new Rectangle(0, 0, stage.width, stage.height);
var pt1:Point = new Point(0, 0);
bitmapData3 = new BitmapData(stage.width, stage.height * 2);
bitmapData3.copyPixels(bitmapData1, rect1, pt1)
var rect2:Rectangle = new Rectangle(0, 0, stage.width, stage.height);
var pt2:Point = new Point(0, stage.height);
bitmapData3.copyPixels(bitmapData2, rect2, pt2)
// Convert to image
var img:ByteArray = jpgEncoder.encode(bitmapData3);
var file:FileReference = new FileReference();
file.save(img, "capture1.jpg");
}
Does anyone have any ideas as to why the OnLoadComplete function is never called? FYI, here is the full source code: https://github.com/john1726/bigbluebutton/blob/master/bigbluebutton-client/src/org/bigbluebutton/main/views/MainToolbar.mxml
TIA
Please note that I've found that the stage was still null in the init() method so an exception was being thrown:
stage.loaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
Also, after resolving that stage error I found that I have been receiving this error using this tool: https://github.com/capilkey/Vizzy-Flash-Tracer
Error #2176: Certain actions, such as those that display a pop-up window, may only be invoked upon user interaction, for example by a mouse click or button press.
So the solution is either to re-work the UI so that there is a button press to prepare the files and a second button press to actually save the image or setup them mouseup and mousedown events to call different functions:
s:Button mouseDown="prepare_PDF()" mouseUp="save_PDF()"
Source: Flex's FileReference.save() can only be called in a user event handler -- how can I get around this?
Thank you!

Remove a function from the stage

I have a big issue with my code
I have a function called "delayCallFuntions":
function delayCallFuntions(delay: int, func: Function) {
var timer: Timer = new Timer(delay, 1);
timer.addEventListener(TimerEvent.TIMER, func);
timer.start();
}
And I used this function like below to make connection between 2 point in my screen:
delayCallFuntions(1, function (e: Event) {timer011(wireColor);});
And function "timer011" is making the connections:
function timer011(firstColor: int): void {
wireColor = firstColor;
//GRID is a class
//Path A to B
var PathAB: Grid;
PathAB = new Grid(4, 5, 20, 17, canvas, wireColor);
this.addChild(PathAB);
}
My problem is:
I have several of these functions like "timer012", "timer013", ... that they need to execute one after another.
When I go out of this scene and come back again, these is still some of these functions are working while I need them to start from the beginning and go one by one.
for example: when i come back, "timer011" is starting while "timer016" is also completing at the same time.
hope someone can help me as this problem made me frustrated.
Currently you are creating a whole new timer everytime you add a function. That timer will stay in memory because of the event listener, and since it's encapsulated in the function, you have no easy way to reference it again to stop them.
What would be a better approach, is to create just one timer globally referenced so you can stop it if needed.
Here is a way you could accomplish this:
//create an array that will hold all the functions you are planning on calling
var delayedFuncs:Array = [];
//this var will be used to store the current function that will be called next
var currentFuncObj:Object = null; //set it to null so it clears the value when you return to this frame
//create a single, global timer reference for everything
//don't initialize it here though
//if you revisit this frame, you don't want to create a whole new timer, but keep using the previous one
var funcTimer:Timer;
//if the timer has already been created (you've been to this frame before), stop it
if (funcTimer) {
funcTimer.stop();
}else {
//if you haven't been to this frame before, create the timer and add the listener
funcTimer = new Timer(1,1);
funcTimer.addEventListener(TimerEvent.TIMER, nextFunc, false, 0, true);
}
//this function adds items to your queue. I've added the ability to also include parameters
function delayCallFunctions(delay:int, func:Function, ... funcParams):void {
//add an object to the array that stores the function, delay, and any parameters to pass to that function
delayedFuncs.push({delay: delay, func: func, params: funcParams});
//if the timer hasn't started running yet, start it since we've added something
if(!funcTimer.running) nextFunc();
}
//this function runs when the timer completes
function nextFunc(e:Event = null):void {
//if we have an existing function to call, call it
if (currentFuncObj){
//invoke the function with the parameters
currentFuncObj.func.apply(null, currentFuncObj.params);
}
//if there are still items in the array, grab the next one
if(delayedFuncs.length > 0){
//array.shift grabs the first element in the array and removes it from the array
currentFuncObj = delayedFuncs.shift();
//reset the timer
funcTimer.reset();
//set the appropriate delay
funcTimer.delay = currentFuncObj.delay;
//start the timer again
funcTimer.start();
}
}
So now, you'd use by doing:
delayCallFunctions(3000, trace, "hello", "world", "I'll be traced 3 seconds from now");
delayCallFunctions(2000, trace, "I'll be called 2 seconds after the last one");
Or, with your specific code:
delayCallFuntions(1000, timer011, wireColor);
Now at any time (say you hit a button to go to change scenes), you can just stop the global timer.
funcTimer.stop();

AS3 running an event for a set duration after button (or anything) event

For the purposes of the question, Imagine I have an object onstage. When I click on another button, I want the colour to change for 1 second, then revert back again when finished.
Here's what my demo code looks like:
Button.addEventListener(MouseEvent.CLICK, Colour_Change);
function Colour_Change(evt: MouseEvent): void {
var my_color: ColorTransform = new ColorTransform();
my_color.color = 0xFF0000;
Coloured_Object.transform.colorTransform = my_color;
}
What I am wanting is some sort of timer function to be incorporated in the above function. I haven't got any idea how to do it, hence why there's no implementation.
You should use the flash.utils.Timer class.Adobe ActionScript 3.0 Reference for the Timer class
The following should be enough to get you headed in the right direction:
import flash.utils.Timer;
var myTimer:Timer = new Timer(1000, 1); // 1 second
var running:Boolean = false;
Button.addEventListener(MouseEvent.CLICK, Colour_Change);
myTimer.addEventListener(TimerEvent.TIMER, runOnce);
function Colour_Change(evt: MouseEvent): void {
var my_color: ColorTransform = new ColorTransform();
my_color.color = 0xFF0000;
Coloured_Object.transform.colorTransform = my_color;
if(!running) {
myTimer.start();
running = true;
}
}
function runOnce(event:TimerEvent):void {
// code to revert the button's color back goes here
myTimer.reset();
running = false;
}
Let me know if you need more help or if this example has errors via this answer's comments section.
To further explain the Timer class as used above:
when creating a new timer
myTimer=new Timer(1000,1)
the first number in the brackets is the number of milliseconds you want the timer to run for (e.g. 1000 = 1 second)
The second number is how many times you want the timer to repeat (or 0 for infinite repetition).
Every time the timer reaches the time you entered (1000), this is will trigger any event listeners for the event Timer_Event.TIMER, so for example if u wanted to make it change color on and off, you could have multiple repetitions on the timer and change the function.
Other useful things timers can do:
You can add an event listener for
Timer_Event.TIMER_COMPLETE
(goes off when all repetitions are complete)
myTimer.currentCount
will return the number of repetitions the timer has done so far.
To do that you can use, as other answers said, a Timer object or the setTimeout() function, like this :
// the current color of our target object
var default_color:ColorTransform;
// the delay in milliseconds
var delay:int = 1000;
btn.addEventListener(MouseEvent.CLICK, Colour_Change);
function Colour_Change(evt: MouseEvent): void {
// save the current color of our target object
default_color = target.transform.colorTransform;
var new_color:ColorTransform = new ColorTransform();
new_color.color = 0xFF0000;
target.transform.colorTransform = new_color;
var timer:Timer = new Timer(delay, 1);
timer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void {
// after the delay, we use the default color of our target
target.transform.colorTransform = default_color;
})
timer.start();
// using setTimeout(), you have to disable this if using the Timer
var timeout:int = setTimeout(
function(){
clearTimeout(timeout);
// after the delay, we use the default color of our target
target.transform.colorTransform = default_color;
},
delay
);
}
Hope that can help.

Actionscript 3 timer is unidentified, how do I fix this?

I'm building a memory Flash game, in which there's a timer that gives you a certain amount of time to finish the deck of cards. The code for this timer is shown below:
public function memory():void
{
levelDuration = 10;
gameTime = levelDuration;
var gameTimer:Timer = new Timer(1000,levelDuration);
gameTimer.addEventListener(TimerEvent.TIMER, updateTime);
gameTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timeExpired);
gameTimer.start();
}
function updateTime(e:TimerEvent):void // what happens when the time runs out
{
// your class variable tracking each second,
gameTime--;
//update your user interface as needed
timeText.text = "Tijd : " + String(gameTime); //gameTime is defined before the public function memory
}
function timeExpired(e:TimerEvent):void
{
var gameTimer:Timer = e.target as Timer;
gameTimer.removeEventListener(TimerEvent.TIMER, updateTime);
gameTimer.removeEventListener(TimerEvent.TIMER, timeExpired);
finalScore = score;
musicchannel.stop();
gameTimer.stop();
MovieClip(root).gotoAndStop("gameover");
}
Now, this works fine. The timer counts down, and when it expires, it brings you to the gameover screen. However, when you finish the game BEFORE the time expires, the timer doesn't stop. It will bring you back to the gameover screen when it decides it has run out, even when you've started a new game.
I've tried to fix this by putting gameTimer.stop() in other functions that bring you to the gameover screen, but then there was another problem. This occurred while trying to stop the timer in other functions, like this one (stop button while playing):
function stopplaying(event:MouseEvent){
gameTimer.stop();
finalScore = score;
musicchannel.stop();
MovieClip(root).gotoAndStop("introduction");
}
This will give me a compile error 1120: access of undefined property gameTimer.
I understand that the gameTimer usually can only be influenced within a function if that function listens to a TimerEvent, but I don't see any options to do this in any other way.
I've tried to make gameTimer a public variable, but it won't allow me to do that within the main memory function.
Also, when I try to define it a public variable out of a funcion, but within the class, the timer will still count down. But when it expires, it just gives a random high number in return and doesn't go to the gameover screen.
I hope the explanation of my problem wasn't too vague and that you are able to help me with this. This is a schoolproject and it's due pretty soon! Also, I can't figure this out on my own with internet alone :( Several tries have only made things worse, I'm afraid to screw this up now that I've come this far.
You were doing it probably right-ish by declaring it as a public variable.. but where did you declare it?
In AS (all versions) you have function scope. That means if you declare your variable inside a function, that variable "exists" (is accessible) only inside of that function. Check where you are declaring your variable:
public function memory():void
{
levelDuration = 10;
gameTime = levelDuration;
var gameTimer:Timer = new Timer(1000,levelDuration); // <-
gameTimer.addEventListener(TimerEvent.TIMER, updateTime);
gameTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timeExpired);
gameTimer.start();
}
That means your variable "gameTimer" is local to the function memory(). You won't be able to use your variable anywhere outside of this function because it exists only inside of memory(). To solve this issue, you have move it outside of the function:
private var gameTimer:Timer;
public function memory():void
{
levelDuration = 10;
gameTime = levelDuration;
gameTimer = new Timer(1000,levelDuration);
gameTimer.addEventListener(TimerEvent.TIMER, updateTime);
gameTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timeExpired);
gameTimer.start();
}
That will solve all your issues.

Refreshing every XX seconds

I am new to actionscript and flash, but i managed to make code that gets data from php file and refresh result every 30 seconds:
var timerRefreshRate:Number = 30000;
var fatherTime:Timer = new Timer(timerRefreshRate, 0);
fatherTime.addEventListener(TimerEvent.TIMER, testaa);
fatherTime.start();
function testaa(event:Event):void{
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE,varsLoaded);
loader.load(new URLRequest("data.php"));
function varsLoaded (event:Event):void {
this.opaqueBackground = loader.data.color;
title.text=loader.data.title;
banner_text.text=loader.data.text;
}
}
But now i am facing 2 problems:
1.) User must wait 30 seconds for movie to load first time
2.) Setting background color does not work any more.
What am i doing wrong?
You can call your function once to load immediately without waiting 30 seconds. Just change the parameters of the function to default to a null event:
function testaa(event:Event = null):void{
//...
}
Now you can call the function like so:
//...
fatherTime.start();
testaa();
So you start the timer but immediately run the function once.
For your second problem, the issue is most likely that you are using a nested function, so this does not refer to your class but rather the testaa function. Nested functions are bad practice in general and you should avoid them if possible. Move the function and loader reference outside and it should work. Final result should be something like this:
var loader:URLLoader;
var timerRefreshRate:Number = 30000;
var fatherTime:Timer = new Timer(timerRefreshRate, 0);
fatherTime.addEventListener(TimerEvent.TIMER, testaa);
fatherTime.start();
testaa();
function testaa(event:Event = null):void{
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE,varsLoaded);
loader.load(new URLRequest("data.php"));
}
function varsLoaded (event:Event):void {
this.opaqueBackground = loader.data.color;
title.text=loader.data.title;
banner_text.text=loader.data.text;
}