TimerEvent function ignoring Timer delay - actionscript-3

I'm working in AS3, Flash AIR 3.2 for iOS SDK. I'm trying to run part of the program only after myLoader finishes loading an image. I have a myTimer.start(); which runs inside myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);.
What seems to be the problem at the moment is that the program is ignoring the 1000ms. The program is running after myLoader is finished at the moment, but it just seems to be doing its own thing in terms of the delay.
EDIT: Being more precise here... The program seems to be ignoring the Timer delay. Even if Timer is set to 100000ms. It seems to be running the rest of the program right after the image is loaded.
EDIT: I still had my methods running inside my Main() as well as the timerListener() in the code. Thought I commented them out. Whoops!
var myTimer:Timer = new Timer(1000);
public function Main()
{
init();
displayImage();
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timerDone);
}
public function displayImage():void {
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
myLoader.load(fileRequest);
}
public function onLoaderComplete(e:Event) {
//start Timer event here
myTimer.start();
}
public function timerListener (e:TimerEvent):void{
trace("Timer is Triggered");
myTimer.stop();
aMethod();
anotherMethod();
moreMethods();
}

You don't really clarify what you mean by "the timer is doing its own thing." Is the timer shorter or longer than you expect?
What I think is likely going on here is that your timer tick and your frame rate are out of synch. If you're familiar with the concept of the elastic racetrack, you know that the single-threaded nature of Flash (unless you're using worker threads) means that the screen can't update during a script and vice-versa. This means that if your Timer fires while the Display list is updating, it just has to wait until the display list finishes and may even have to wait until other scripts have run, depending on how Flash is prioritizing the different things in its queue.
From the Timer API:
Depending on the SWF file's framerate or the runtime environment
(available memory and other factors), the runtime may dispatch events
at slightly offset intervals. For example, if a SWF file is set to
play at 10 frames per second (fps), which is 100 millisecond
intervals, but your timer is set to fire an event at 80 milliseconds,
the event will be dispatched close to the 100 millisecond interval.
Memory-intensive scripts may also offset the events.

Related

SWFLoader doens't work twice

I have a game that dynamicaly loads another minigame.
I load it using SWFLoader:
function loadMinigame(minigameName:String):void
{
var loader:SWFLoader = new SWFLoader;
loader.load("/Content/Swf/" + minigameName + ".swf");
layerMinigames.addElement(loader);
}
The first time I load it works nice. But when I exit the minigame and try to open it again via same method it doens't apper in my application (sometimes do sometimes doens't).
Both games are Flex application.
What's going on?
Update
I did loader.loadForCompatibility = true and now it loads properly! But now I'm getting an error when I try to cast the result:
loader.addEventListener(Event.COMPLETE, function(e:Event):void
{
var sys:SystemManager = SystemManager(e.currentTarget.content);
});
gives:
TypeError: Error #1034: Type Coercion failed: cannot convert
_AppMinigame1_mx_managers_SystemManager#14c97eb9 to mx.managers.SystemManager.
You'd better cache the loaded minigame after loading, then just display it again when needed. Make sure though, that the minigame's ADDED_TO_STAGE listener resets the minigame properly.
static var loadedGames:Object={};
function loadMinigame(minigameName:String):void
{
if (loadedGames[minigameName]==null) {
var loader:SWFLoader = new SWFLoader;
loader.load("/Content/Swf/" + minigameName + ".swf");
loadedGames[minigameName]=loader;
loader.addEventListener(Event.COMPLETE,loaderFinished);
} else {
layerMinigames.addElement(loadedGames[minigameName]);
}
}
function loaderFinished(e:Event):void {
layerMinigames.addElement(e.target);
// do the rest, like setting proper start time for minigame, etc
}
UPDATE: Since you run out of memory while running such a process, this means that the minigame's instance holds onto too much memory, and needs to be freed (GC'd) in order to load another minigame. Thus, caching loading SWFs is out of question. You can release an SWF using unloadAndStop() call against it, prior to doing this, make sure that your minigame is removed from display list, and its REMOVED_FROM_STAGE listener shuts down every listener attached to stage. Also you might need to debug your minigame SWF to locate probable memory leaks, as your SWFs share the same memory space, and a memory leak in one of them nullifies your efforts to do anything in all the others.

Why isn't this garbage collected

If I make a timer such as
var timer:Timer = new Timer(50, 0);
timer.addEventListener(TimerEvent.TIMER, OnTimer);
timer.start();
and then my function ends, you would think this timer has gone out of scope and nothing is holding on to a reference of it anymore. However this timer still works.
So either I am getting lucky and the garbage collector hasn't run yet or something is holding on to a reference. If it is the latter then how will I know it is going to be garbage collected?
Timer will still run and will be dispatching events. Just declare it the way you can access it (as public instance variable) and perform:
timer.stop();
timer.removeEventListener(TimerEvent.TIMER, OnTimer); - VERY important thing in Flash
timer = null; - if you really need to free memory, set the reference to null

as3 air unloadAndStop() event listener or timer

my air application plays an external swf over and over until time to play the next external swf. in order to avoid memory leaks i am using unloadAndStop(). i am using two timers. the first unloadAndStops the swf. the second waits two seconds then loads it back up again.
this approach (coupled with the use of weak references) seems to keep the memory in check. however, i'd rather not use timers but event listeners. is there an event listener for when unloadAndStop completes to then load the swf again.
here is what i had in mind:
var TIMER_INTERVAL:int = int(duration);
var t:Timer = new Timer(TIMER_INTERVAL);
t.addEventListener(TimerEvent.TIMER,updateTimer,false,0,true);
t.start();
private function updateTimer(e:TimerEvent):void
{
swfLoader.unloadAndStop(true);
swfLoader.addEventListener(Event.UNLOAD,onSWFUnloadComplete,false,0,true);
}
private function updateTimer(e:TimerEvent):void
{
var swfSource2:String = File.applicationStorageDirectory.nativePath.toString();
swfLoader.load(swfSource2+'\\'+name_xml);
}
unloadAndStop is not an asynchronous method, so an unload event wouldn't really be relevant. What is likely happening behind the scenes is that it takes 1 full frame to fully dispose of the movies objects/listeners and that's why you're having issues loading it again in the same block of code.
If you wait just one frame before loading it again, you should have the results you expect.
Now, of course the best solution is go into the source file of your loaded swf and fix the memory leaks.

Why does my EVENT.ACTIVE trigger twice?

When I add an EVENT.ACTIVATE listener to my project, and then alt-tab away and back to my project it triggers twice.
edit: shaunhusain and I seem to have found the cause of the problem, although without a solution. When running the standalone player version 11+ the event triggers 2x. When running standalone player version <11 or any version in the browser it triggers 1x. So it appears there may be a bug in recent versions of the flash player projector. I'm going to nail down the exact versions and report it to adobe and see what happens. Thanks to anyone who read this and tried to help!!
I want it to fire every time I change focus, I just don't want it to fire twice every time I change focus.
Why is this? Am I doing something wrong? What's the best way to prevent this behavior?
It seems like it would be a common question, but Google turned up nothing.
Code:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
stage.addEventListener(Event.ACTIVATE, test);
}
private function test(e:Event):void
{
trace(e.target);
}
}
}
Actual result:
[object Stage]
[object Stage]
Desired result:
[object Stage]
It doesn't seem to make a difference whether I add the listener to the stage or anything else, the behavior is the same.
The same thing also happens with EVENT.DEACTIVATE. Others such as mouse up work fine.
My goal is to pause a game on EVENT.DEACTIVATE and unpause it on EVENT.ACTIVATE. The problem is that when the event fires twice, it calls the unpause function twice which has unwanted consequences.
ActionScript® 3.0 Reference for the Adobe® Flash® Platform says about this event:
Dispatched when the Flash Player or AIR application gains operating
system focus and becomes active. This event is a broadcast event,
which means that it is dispatched by all EventDispatcher objects with
a listener registered for this event. For more information about
broadcast events, see the DisplayObject class.
For me it looks like you want to prevent its designed behavior? I suppose it was designed to fire every time you change focus, or am I wrong? What do you want to accomplish? However this is an answer, so based on what you wrote - you can do a workaround by just removing a listener after he fired once:
private function test(e:Event):void
{
stage.removeEventListener(Event.ACTIVATE, test);
trace(e.target);
}
But I would recommend you to write something more about why are you using it and what want to accomplish if this is not satisfactory.
I've had the same issue in my AIR Mobile app.
To correct this issue, I've stored the last event name triggered for an Activate / Deactivate event. If it is attempted twice in a row, it just gets skipped with a return;
private static function onAppStateChanged(e:Event):void {
if (_STATE == e.type) {
return;
}
_STATE = e.type;
switch(_STATE) {
case Event.ACTIVATE: whenActivated.dispatch(); break;
case Event.DEACTIVATE: whenDeactivated.dispatch(); break;
}
}
At first, the value of _STATE begins with null, so that should allow it to pass through the first time around.
I bind both Event.ACTIVATE and Event.DEACTIVATE to the same listener, onAppStateChanged in my App's initialization method, like so:
_STAGE.addEventListener(Event.ACTIVATE, onAppStateChanged);
_STAGE.addEventListener(Event.DEACTIVATE, onAppStateChanged);
You can replace the Switch-Statement however you like, I just personally prefer using Robert Penner's AS3Signals (GitHub) to broadcast the resume/suspend signal across the App.

ActionScript 3: Measuring elapsed time between enterFrame events

I have an EnterFrame event, and I want to know the exact time between calls, so I will be able to animate objects more smoothly when the computer can't produce the desired framerate.
To be a little more specific.
currentTime = getTimer();
diff = currentTime - prevTime;
prevTime = currentTime;//update for next go around
EDIT
getTimer requires you import the package: flash.utils.getTimer;
use getTimer(); ?
as the others stated, getTimer is the best way to go ... but i wanted to suggest something else:
if you want to do time based animation, that are updated frame based, you might also try some of the bigger ActionScript tweening libraries, as the caurina Tweener ... they simply do that out of the box and provide other great features ...
greetz
back2dos
You should use the inbuilt Timer function to call a method at a somehow* ENTER_FRAME independent way.
var timer:Timer = new Timer(500, 0);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
private function timerHandler(event:TimerEvent):void
{
// do something
// *EDIT* Thanks #Luke spotting this out (check comments)
event.updateAfterEvent();
}
*) Still you need to keep in mind the event handler will not be triggered between frames so in case your script is lagging (because of some other process) this call will also be delayed.
To be precise, the method call will be approximated at the frame executed at the same time or right after the set delay time.