ActionScript 3.0: A Dynamic Frame Rate Switching Class to Lower CPU Usage
r3dux | January 20, 2010Flash gets a lot of negative press because it’s seen as using a heap of CPU time and bogging everything down. And it’s a fair cop. Most flash will eat up your CPU cycles even when it’s sitting there doing nothing. But this isn’t a fault of flash, but rather of flash developers. Let me explain…
When you start a piece of flash work, you assign it a frame rate at which you want it to run, so it’ll update the screen, say, 30 times a second. This is all fine and good for when you’re animating things on the stage. But what about when you’re not? Well, it’s still running at 30 frames per second and chewing up your CPU like a crazy melon farmer. This is Not A Good Thing. So, anyhow, I’m watching this video about SWF Framerate Optimisation, and the guy’s showing how you can modify your code to lower the frame rate when there’s not a lot happening, and bring it back up when you’re animating. So I had a crack at it, and lo & behold, it works fine for the specific piece of flash I’d coded it into, so I wondered if I couldn’t just go and make a RateController class. This way, I could add a RateController object to any project to dynamically change the project’s frame rate depending on whether the mouse was over the stage or not.
And after much swearing about not having global access to the stage properties, I found that I COULD!!!
Here’s a working example placed into the attracting particles code I wrote yesterday:
Note: The animation starts at full speed for two seconds on startup. It’ll drop to the sleeping rate (5 fps) two seconds after the mouse leaves the stage, and then ramps back up to its waking rate (30 fps) instantly when the cursor is back over the stage. The FPSCounter shows intermediate numbers because it’s based on an average.
To add a RateController to any flash project, you can just use something like:
import RateController; // Add a new RateController, uses the root stage, runs at 5fps when sleeping, 30fps // when active (i.e. when mouse is over the stage), and uses a 2000 millisecond // delay after the mouse leaves the stage before dropping the FPS to the sleeping rate. addChild(new RateController(stage, 5, 30, 2000));
Not bad, eh?
Full class code & file downloads after the jump…
Behold the RateController Class in all its over-commented glory!:
package { import flash.display.Stage; import flash.display.MovieClip; import flash.utils.Timer; import flash.events.TimerEvent; import flash.events.MouseEvent; import flash.events.Event; public class RateController extends MovieClip { static var switchStateTimer:Timer; // Timer used for when to actually switch states after mouse has left stage static var firstRunTimer:Timer; // Timer used to allow full framerate briefly on startup static var active:Boolean = true; // Whether the animation is active static var instantChange:Boolean = true; // Whether we're going to instantly change the frame rate static var firstRun:Boolean = true; // Animate normally for duration of timer at startup var sleepFrameRate:uint; // The frame rate for when the mouse is not on the stage var activeFrameRate:uint; // The frame rate for when the mouse IS on the stage var switchStateDelay:Number; // How long to animate at full rate after the mouse cursor has left the stage var currentStage:Stage; // A Stage that points to our root stage, we need this to set the framerate // Constructor public function RateController(theStage:Stage, theSleepFrameRate:uint, theActiveFrameRate:uint, theSwitchStateDelay:Number) { this.currentStage = theStage; this.sleepFrameRate = theSleepFrameRate; this.activeFrameRate = theActiveFrameRate; this.switchStateDelay = theSwitchStateDelay; // Create (but not start!) our timer to switch sleep/wake after a given delay switchStateTimer = new Timer(this.switchStateDelay); // This sleep listener will always listen for the mouse leaving the stage this.currentStage.addEventListener(Event.MOUSE_LEAVE, switchController); trace("Our current stage is: " + this.currentStage); // When we start, we want to run at full speed for a little bit to allow any // initial animation to happen at the correct rate, and then after a small while // put the animation to sleep (if the mouse is moving over it it'll instantly wake // up anyway) if (firstRun == true) { firstRunTimer = new Timer(this.switchStateDelay); firstRunTimer.addEventListener(TimerEvent.TIMER, switchStates); firstRunTimer.start(); } } // End of constructor // Function to control the frame rate switching private function switchController(e:Event):void { trace("At switchController function..."); trace("Frame rate is currently: " + stage.frameRate + "\n"); // If we're going to sleep, set the sleep frame rate AFTER the timer delay if (instantChange == false) { trace("Sleeping after timer expires."); // If we're going to sleep, switch states after the timer delay switchStateTimer.addEventListener(TimerEvent.TIMER, switchStates); switchStateTimer.start(); } else // If we're waking up, we want to set our active frame rate instantly! { trace("Waking immediately."); // switchStates expects a timer event, so we have to pass it a bogus one. // Bit of a bodge, I know =/ var foo:TimerEvent; switchStates(foo); } } // End of switchController function // Function to actually switch the states private function switchStates(e:TimerEvent):void { // If we haven't been called from our initial startup timer if (firstRun == false) { trace("At switchStates function. Stopping timer and switching states..."); trace("active currently has the value: " + active + "\n"); switchStateTimer.stop(); switchStateTimer.removeEventListener(TimerEvent.TIMER, switchStates); // Swap sleep to wake or vice versa if (active == false) // If the animation's asleep, wake it up! { // Set our flags active = true; instantChange = false; // Put the animation to it's waking frame rate and remove the listener for mouse activity on the stage this.currentStage.removeEventListener(MouseEvent.MOUSE_MOVE, switchController); this.currentStage.frameRate = activeFrameRate; trace("Setting flash to wake state, where active has the value: " + active); trace("Setting wake frame rate: " + stage.frameRate + "\n"); } else // If the animation's awake, put it to sleep! { trace("active must be true and we're switching states..."); // Set our flags so we know we're not active, and we want to instantly jump // to our active framerate when the mouse returns to the stage active = false; instantChange = true; // Put the animation to it's sleeping frame rate and add a listener for mouse activity on the stage this.currentStage.frameRate = sleepFrameRate; this.currentStage.addEventListener(MouseEvent.MOUSE_MOVE, switchController); trace("Timer has expired, so setting flash to sleep state."); trace("Setting sleep frame rate: " + stage.frameRate + "\n"); } } else // If we HAVE been called on initial startup (firstRun == true) { trace("Putting to sleep after initial firstRun timer\n"); // Flip the flags so that: firstRun = false; // We will no longer be in firstRun mode active = false; // Assume mouse not initially over stage, so the animation is in sleep mode instantChange = true; // When we activate, we want the full frame rate instantly // Put the animation to it's sleeping frame rate and add a listener for mouse activity on the stage this.currentStage.frameRate = sleepFrameRate; this.currentStage.addEventListener(MouseEvent.MOUSE_MOVE, switchController); // Remove our firstRun timer firstRunTimer.stop(); firstRunTimer.removeEventListener(TimerEvent.TIMER, switchStates); } } // End of switch states function } // End of RateController class } // End of package
There’s a lot of debug code in there at the moment so you can see what it’s doing while it’s doing it, but the version you can download below has all that stripped out.
RateController Class source file: RateController.as
Now for a beer, debugging that thing’s given me a headache…
Related posts:











[...] I’ve modified the above example (but not the associated source code and files) to use the RateController Class I wrote yesterday. Aside from adding a RateController object, the only other modification was [...]
I frikkin love this dude! Thank you so much for putting it together and making it so simple to implement. Much cudos!
No worries – glad you found it useful =D