As a long-time AS3 coder, I’m very used to using AS3’s Timer class. For my Starling game tutorial, I even extended it and created the GameTimer class where I added the ability to pause the timer and some other things. As I’ve been working more on my game based on my tutorial engine, I’ve realized that I’ve made a huge mistake in the way I coded the Timers. Namely, in Starling… well…
Let’s say you’re implementing an enemy spawn timer into your game. You’ve got a number of enemies in the wave (say, 5) and a set amount of time (say, 1500ms) that you want to wait in between spawning each enemy. Without Starling, you would be using some variation of AS3’s Timer class, and your implementation would look something like the following:
// create a new Timer instance that has a 1500ms delay and repeats 5 times var timer:Timer = new Timer(1500, 5); // a new enemy spawns every time onTimerTick gets called timer.addEventListener(TimerEvent.TIMER, onTimerTick); // to keep track of when the whole enemy wave is done spawning timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete); // when ready to start the next wave... timer.start();
While this would work perfectly fine in your Starling game, Starling has a secret Timer class built-in called DelayedCall. I call it “secret” because it exists in the starling.animation package (our spawn timer isn’t animated why would we use something from animation?) and doesn’t immediately jump out at you when looking over the API docs that this secret class can be used as a timer, but it’s exactly what the class is written for.
So first, why would you not want to just use Timer? Let’s use a band analogy. You’re the guitarist in a kickass rock band. Way to go! Now, the drummer is going to keep a steady beat (all drummers do, right?) that everyone else in the band is going to follow for the song. You, however, decide that you’re going to start a metronome exactly in time with that first drum beat, and you’re only going to listen to the metronome. Sure, at first you’ll probably be exactly in sync. But what happens when you’ve practiced that sweet break where the music stops, then kicks back in but not on the same beat your metronome is set to? Now you’re screwed. The drummer keeps the time man, all the ladies in the audience know that… if you start wailing on your guitar off beat, it doesn’t matter how good that solo it is… you’re off beat man.
Starling’s IAnimatable interface ensures that you can hear that steady drum beat to keep everything in your game in sync. Adding your own Timer class is you being that guitarist that’s got his own time on the metronome… sure it’s in sync now, but what happens in that mobile app when a user clicks the home button, then goes back to the game later? Did your timer stop? Did you make sure you stopped every separate timer when your app lost context? Did you leave the gas on?
There’s no need to worry if you implement IAnimatable in your classes and I’ll be happy to show you how! Let’s look at that same enemy spawn timer but using Starling’s DelayedCall class.
// create a new DelayedCall instance that has a 1.5s delay // and uses the function onTimerTickDC as it's callback (onTimerTickDC gets called every 1.5s) var timerDC:DelayedCall = new DelayedCall(onTimerTickDC, 1.5); // set the repeatCount of the DelayedCall to 5 timerDC.repeatCount = 5; // to keep track of when the whole enemy wave is done spawning timerDC.addEventListener(Event.REMOVE_FROM_JUGGLER, onTimerCompleteDC); // when ready to start the next wave... Starling.juggler.add(timerDC);
You may notice a few things right off that are different.
- 1 – DelayedCall uses seconds not milliseconds like Timer. Make sure you do your math and divide by 1000 or update your times to seconds.
- 2 – there is no way to set the repeatCount through the constructor. It defaults to 0 when you create the instance of DelayedCall, so unless you need a repeating timer, you don’t need that extra timerDC.repeatCount = 5; line.
- 3 – when the DelayedCall reaches the number of times it’s supposed to repeat, it will remove itself from the Juggler. You don’t get a “Complete” Event, but you can and should listen for the Event.REMOVE_FROM_JUGGLER if you want to know when your timer is finished. You’re done and don’t have to do anything else with it! Simple! Set, start, forget!
A final difference to note is that in the DelayedCall constructor, you can pass in an Array of args as the 3rd argument. Those args will then get passed to the callback function. I have not found a use for these so I didn’t include it in the list, but if you’ve got a use for callback args, I’d love to hear it!
I wrote a little project file if you’d like to see the code in action. You can find that here. Also, as I’m learning more Starling and writing better code, I’ll be going back through my tutorial to update it to the more “proper” ways to do things.