ActionScript 3: Measuring elapsed time between enterFrame events - actionscript-3

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.

Related

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

TimerEvent function ignoring Timer delay

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.

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.

How do I completely remove a loaded swf and reload it? (Trying to restart a game)

I have a preloader that loads a swf, the swf creates a bunch of listeners, objects, and movie clips. I want all of these to be destroyed and recreated.
Simplified version of my preloader:
var request:URLRequest = new URLRequest("myfile.swf");
var myLoader:Loader = new Loader();
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, function(event){
stage.addChild(myLoader);
myLoader.loadBytes(urlLoader.data);
});
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(request);
When I try to remove it, I do this:
stage.removeChild(myLoader);
var child = loader.myLoader.content as Object;
SoundMixer.stopAll();
while(stage.numChildren > 0){
stage.removeChildAt(0);
}
child.stop();
while(stage.numChildren > 0){
stage.removeChildAt(0);
}
child=null;
System.gc();
myLoader.unloadAndStop(true);
System.gc();
myLoader.unload();
System.gc();
myLoader.loadBytes(urlLoader.data);
stage.addChild(loader.myLoader);
In your loaded SWF you may create a method 'destroy' which would remove all listeners, destroy all objects and reset all data.
You can call this method either from the parent object (if the method is public) or you can call destroy when you remove the SWF from stage (Event.REMOVED_FROM_STAGE)
You can reload swf with js, this is the easy way to do this. you can check this answer.
Or you have to do a good object and listener managment and reset game in swf file. This might be complex as project get bigger. You need release methods that removes all references and listener.
first you should load your swf using the Loader Class like this :
var movie:MovieClip
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load( new URLRequest("myfile.swf"));
function loadComplete(event:Event):void
{
movie = loader.content as MovieCLip;
addChild(movie);
}
//when reloading just remove the movie object from the stage
removeChild(movie);
SoundMixer.stopAll();
//......
//.
//.
I know this is old, but I stumbled on it for reference and see some things that I think should be mentioned.
System.gc() - while it's a function you have access to, everything I've seen suggests that the garbage collector should never be called by your code unless you've used a considerable amount of memory and need it back immediately. The garbage collector usually has a better idea of when it should run than you do (Force Garbage Collection in AS3?). It can impact performance for not just your application but all flash applications running in that browser or container.
There's also a good bit of people struggling to make effective use of unloadAndStop(), as it seems to be rather unreliable in various contexts (Unloading swf using the unloadAndStop() method, but video sounds remain audible).
To be thorough, I'd strongly suggest putting the effort into simply eliminating everything as it would be in any other language from within the loaded flash object first before removing the flash object itself, not expecting the container to take care of it for you. This is why Flash has a reputation for memory leaks to begin with.
Inside the loaded swf, add code that fires on unload.
One way is to add this to the swf:
this.addEventListener(Event.REMOVED_FROM_STAGE, doUnload)
Then have the doUnload functon perform the following:
Use removeEventListener to effectively terminate the event handlers. You could either recall all the addEventListeners you created, or cook up a routine to walk through and remove them all by traversing the objects in play. The first option is more efficient since it's exclusive, but the second option would theoretically be something you could memorize and copy between applications.
Use delete() on variables to make them removable by the garbage collector. NOTE: Unlike many languages, a referenced variable will not be removable unless the reference is also removed. For example:
var a:int = 0;
var b:int = a;
delete(a); // b still exists and equals 0, a is not available to GC.
delete(b); // now both variables can be removed by GC.
There's also still confusion as to whether it's a good idea to use removeChildAt(0) or remove the child objects individually. The first has the benefit of being distinctly simple and straight-forward, but that also gives it the caveat of not being entirely understood by the coder, and possibly missing something similarly to unloadAndStop(). Not as much as walking your object tree with removeChild and eliminating things explicitly without question or uncertainty.
After this is set, adding a function to unload the flash object will trigger its self-removal, so the loader will remain simple and neat, and reload the flash cleanly.

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.