Using Timers
Preface
Timers allow you to run a function at a particular interval, for example, lets say you wanted to run a function every frame, or 3 frames, or maybe run a function 500 frames later, sure you could use a counter variable with the update function like this:
var counter = self.makeInt(120); // 120 Frame counter
function makeDamageContainerInvisible() {
self.getDamageCounterContainer().alpha = 1;
}
function update() {
counter.dec();
if (counter.get() == 0) {
makeDamageContainerInvisible();
}
}
But this approach while functional, might not scale well when you might wanna create several timers, you'd need 1 variable for each one, and if you want it to repeat you'd also have to reset the counter yourself, or do some simple math to trigger at certain intervals.
A Primer on Timers
What can timers be added to?
Currently timers can be added to the following classes:
Basic Structure
For reference this is the structure
addTimer(interval:Int, repeats:Int, func, ?options: {?condition: () -> Bool, ?inverseCondition: () => Bool, ?pauseCondition: () => Bool, ?persistent: Bool}) ):Int
interval
: Time in frames between function callsrepeats
: How many times to call the functionfunc
: The function being passed to the timer, please take a look at the Functions Page on how to pass functions to other functionsoptions
: These are the options that modify the behavior of the timer in various ways, they are optional so you don't need to use themcondition
: A function you can pass that determines whether the function will trigger when the interval is hit, if the function returns true,func
gets called, otherwise it will notinverseCondition
: similar tocondition
except thefunc
if the function you pass returns false when the interval is hitpauseCondition
: when this function returns true, the timer will be pausedpersistent
: determines whether the timer will stop if there is an animation change
The timer also returns an
Int
, which is the timer's id, which can be used to remove the timer manually
That's quite a bit to take in, but the option
you'll likely be using most is persistent
so we'll use the simple example of adding damage over time
Examples
Basic Non Persistent Timer
self.addTimer(1, 100, function () {
self.addDamage(1);
});
Lets breakdown what happens here:
- First we pass 1 for the
interval
, so this timer runs every frame - We passed 100 for the repeats, so it will trigger 100 times
- The function simply adds 1 damage to
self
With all that put together, it causes you to gain 1 damage per frame, for 100 frames.
But if the current animation lasts for less than 100 frames, then the timer will end prematurely, so to prevent that you make it persistent:
Persistent Timers
self.addTimer(1, 100, function () {
self.addDamage(1);
}, {persistent: true});
Now with the persistent: true
, the timer would always run to completion even if the animation changes.
Endless Timers
If you wanted the timer to run forever, pass either 0 or a negative number to repeats like so:
self.addTimer(1, -1, function () {
self.addDamage(1);
}, {persistent: true});
Larger Intervals
If you wanted the interval to be larger, the process is fairly similar
self.addTimer(30, -1, function () {
self.addDamage(1);
}, {persistent: true});
Manually Removing Timers
addTimer
returns an Int
, which is the timerId, which you can pass to removeTimer
var id:Int = self.addTimer(30, -1, function () {
self.addDamage(1);
}, {persistent: true});
// When you wanna remove the timer later
self.removeTimer(id);
Timer Conditions
To put it simply conditions determine when the timer will be active and whether the function would be called.
Conditions and Inverse Conditions
This would be a function that checks if the timer function will be able to run at any given time.
Building upon the example above, let's say you wanted to heal only if you were in one of the falling states
function isFalling() {
return self.inState(CState.FALL) || self.inState(CState.FALL_SPECIAL);
}
self.addTimer(30, -1, function () {
self.addDamage(1);
}, {persistent: true, condition: isFalling});
Of course this is equivalent to this:
function isFalling() {
return self.inState(CState.FALL) || self.inState(CState.FALL_SPECIAL);
}
self.addTimer(30, -1, function () {
if (isFalling()) {
self.addDamage(1);
}
}, {persistent: true});
Inverse Conditions
Inverse conditions work very similarly so I'll keep it brief.It determines when the function shouldn't be called so if you wanted the function to be called only when NOT in falling state:
function isFalling() {
return self.inState(CState.FALL) || self.inState(CState.FALL_SPECIAL);
}
self.addTimer(30, -1, function () {
self.addDamage(1);
}, {persistent: true, inverseCondition: isFalling});
Pause Conditions
Pause conditions work a bit differently from the two above. Rather than determining whether the function should be called, it determines whether the timer should continue counting, or in other words while the condition is true, the timer is halted.
So if we wanted to pause the timer whilst in a HURT or KO state group:
function shouldPause() {
return self.inStateGroup(CStateGroup.KO) || self.inStateGroup(CStateGroup.HURT);
}
self.addTimer(30, -1, function () {
self.addDamage(1);
}, {persistent: true, pauseCondition: shouldPause});