Tony Polinelli
Tarwin Stroh-Spijer

contact [at]
touchmypixel.com

6/25 Easey Street
Collingwood, 3066
Vic, Australia

+61 3 8060 5321

AS3 Frame Based setInterval and setTimeout

Sometimes you want to be able to use a timeout or interval but, as they are tied to milliseconds, if the frame-rate of your movie goes down from what you expected - games is a good example - then you can run into trouble.

I recently had this problem with a game, that when the frame rate lowered, cyclic "emitters" (ie creating bubbles underwater) would not be synced with the expeted speed of the game.

The solution. A setInterval and setTimeout that you can set using milliseconds (or frames if you want) and will be called at the expected number of frames. The only caviet with this approach is that it needs to be inited with the stage object, both so it can get an ENTER_FRAME event and the current FPS.

Example Use

Actionscript:
  1. package {
  2.     import flash.display.MovieClip;
  3.     import com.touchmypixel.utils.FrameTimer;
  4.  
  5.     public class TOTest extends MovieClip{
  6.        
  7.         public function TOTest() {
  8.             FrameTimer.init(stage);
  9.             FrameTimer.setTimeout(2000, test1);
  10.             FrameTimer.setTimeout(2500, test1);
  11.             FrameTimer.setFrameTimeout(35, test2);
  12.             FrameTimer.setFrameTimeout(70, test2);
  13.             FrameTimer.setInterval(2000, test3);
  14.             FrameTimer.setFrameInterval(70, test5);
  15.            
  16.             var iv:int = FrameTimer.setFrameInterval(15, test6);
  17.             FrameTimer.clearInterval(iv);
  18.            
  19.             var to:int = FrameTimer.setTimeout(500, test7);
  20.             FrameTimer.clearTimeout(to);
  21.            
  22.         }
  23.  
  24.         private function test7():void {
  25.             // should never be printed
  26.             trace("NOT SHOWN------ Timeout: milliseconds");
  27.         }      
  28.        
  29.         private function test6():void {
  30.             // should never be printed
  31.             trace("NOT SHOWN------ Interval: frames");
  32.         }      
  33.        
  34.         private function test5():void{
  35.             trace("Interval: frames");
  36.         }
  37.        
  38.         private function test3():void{
  39.             trace("Interval: milliseconds");
  40.         }
  41.        
  42.         public function test1()
  43.         {
  44.             trace("Timeout: milliseconds");
  45.         }
  46.        
  47.         public function test2()
  48.         {
  49.             trace("Timeout: frames");
  50.         }
  51.     }
  52.    
  53. }

Source

It makes use of "internal classes" which I had not heard about until recently. I'm finding them really good in some cases and have heard they are actually faster than using non-defined objects ie {x:0, y:10} as the compiler can make optimizations to them. The only thing you need to remember is that they must be defined outside your package - which can be counter-intuitive and a little confusing at first.

Actionscript:
  1. /*
  2. * FrameTimer.as
  3. * Copyright: 2008 Touch My Pixel - www.touchmypixel.com - contact@touchmypixel.com
  4. * Please see X for discussion
  5. */
  6.  
  7. package com.touchmypixel.utils {
  8.    
  9.     import com.adobe.utils.DictionaryUtil;
  10.     import flash.display.Stage;
  11.     import flash.events.Event;
  12.     import flash.events.EventDispatcher;
  13.     import flash.utils.Dictionary;
  14.    
  15.     public class FrameTimer {
  16.        
  17.         private static var frameRate:int = 35;
  18.         private static var scope:Stage;
  19.         private static var timeouts:Dictionary;
  20.         private static var intervals:Dictionary;
  21.         private static var timoutsCounter:int = 1;
  22.         private static var intervalsCounter:int = 1;
  23.         private static var millisecondsPerFrame:int = 0;
  24.        
  25.         public function FrameTimer() {}
  26.        
  27.         public static function init(_scope:Stage)
  28.         {
  29.             frameRate = _scope.frameRate;
  30.             scope = _scope;
  31.             timeouts = new Dictionary();
  32.             intervals = new Dictionary();
  33.             millisecondsPerFrame = Math.round(1000 / frameRate);
  34.             scope.addEventListener(Event.ENTER_FRAME, doTimer);
  35.         }
  36.        
  37.         static private function doTimer(e:Event):void {
  38.             var to:TimeoutItem;
  39.             for each(to in timeouts) {
  40.                 if (--to.framesLeft == 0) {
  41.                     to.callback();
  42.                     delete timeouts[to];
  43.                 }
  44.             }
  45.            
  46.             var iv:IntervalItem;
  47.             for each(iv in intervals) {
  48.                 if (--iv.framesLeft == 0) {
  49.                     iv.callback();
  50.                     iv.framesLeft = iv.frameInterval;
  51.                 }
  52.             }
  53.         }
  54.        
  55.         /*==================================================================================*/
  56.         public static function setFrameTimeout(callback:Function, frames:int):int
  57.         {
  58.             if (scope != null) {
  59.                 timeouts[++timoutsCounter] = new TimeoutItem(frames, callback);
  60.                 return(timoutsCounter);
  61.             }else {
  62.                 trace("Please INIT with scope");
  63.             }
  64.             return(0);
  65.         }
  66.        
  67.         public static function setTimeout(callback:Function, milliseconds:int):int
  68.         {
  69.             if (scope != null) {
  70.                 timeouts[++timoutsCounter] = new TimeoutItem(Math.round(milliseconds/millisecondsPerFrame), callback);
  71.                 return(timoutsCounter);
  72.             }else {
  73.                 trace("Please INIT with scope");
  74.             }
  75.             return(0);
  76.         }
  77.        
  78.         public static function clearTimeout(key:int)
  79.         {
  80.             delete timeouts[key];
  81.         }
  82.        
  83.         /*==================================================================================*/
  84.         public static function setFrameInterval(callback:Function, frames:int):int
  85.         {
  86.             if (scope != null) {
  87.                 intervals[++intervalsCounter] = new IntervalItem(frames, callback);
  88.                 return(intervalsCounter);
  89.             }else {
  90.                 trace("Please INIT with scope");
  91.             }
  92.             return(0);
  93.         }
  94.        
  95.         public static function setInterval(callback:Function, milliseconds:int):int
  96.         {
  97.             if (scope != null) {
  98.                 intervals[++intervalsCounter] = new IntervalItem(Math.round(milliseconds/millisecondsPerFrame), callback);
  99.                 return(intervalsCounter);
  100.             }else {
  101.                 trace("Please INIT with scope");
  102.             }
  103.             return(0);
  104.         }
  105.        
  106.         public static function clearInterval(key:int)
  107.         {
  108.             delete intervals[key];
  109.         }
  110.        
  111.         /*==================================================================================*/
  112.         public static function clearAll()
  113.         {
  114.             timeouts = new Dictionary();
  115.             intervals = new Dictionary();
  116.         }
  117.        
  118.         public static function clearAllTimeout()
  119.         {
  120.             timeouts = new Dictionary();
  121.         }
  122.        
  123.         public static function clearAllIntervals()
  124.         {
  125.             intervals = new Dictionary();
  126.         }
  127.     }
  128. }
  129.  
  130. internal class TimeoutItem
  131. {
  132.     public var callback:Function;
  133.     public var framesLeft:int;
  134.    
  135.     public function TimeoutItem(_framesLeft:int, _callback:Function)
  136.     {
  137.         callback = _callback;
  138.         framesLeft = _framesLeft;
  139.     }
  140. }
  141.  
  142. internal class IntervalItem
  143. {
  144.     public var callback:Function;
  145.     public var framesLeft:int;
  146.     public var frameInterval:int;
  147.    
  148.     public function IntervalItem(_frameInterval:int, _callback:Function)
  149.     {
  150.         callback = _callback;
  151.         frameInterval = _frameInterval;
  152.         framesLeft = _frameInterval;
  153.     }
  154. }

Tags: ,

2 comments


  1. Yo, this AS3 class is just for timed-based functions? Does it works if I want to just change the frameRate of the flash, everything by AS?


  2. Not exactly sure what you're asking. If you change the framerate of the file then these functions will be slower or faster (if you set frames).

 

Leave a Reply

Our Friends:

Powered by haXe / poko cms | Copyright 2008-09 TouchMyPixel