Top

Secret Chronicles of the Scripting API documentation

Class Timer

Timers are an easy and efficient way to execute MRuby code based on a time span. They can either be periodic, meaning that they will continue to be active for an unspecified amount of time, or non-periodic aka one-shot, meaning that they will only be active for a definite timespan. For both types of timers, you have to call the #start method to activate them, and it’s possible to interrupt a timer by means of its #stop method (you can also abort a one-shot timer this way if it has not yet fired).

Both types of timers employ a callback concept similar to how events work. When creating a timer with ::new or one of the convenience methods ::after and ::every, you pass a callback to the method which will be invoked whenever the timer fires.

A periodic timer enters an infine loop when #start is called on it. It will then wait the inverval specified when creating the timer and execute the callback. That process is repeated until you either call one of the stop methods or end the level.

A non-periodic or one-shot timer doesn’t loop. When #start is called, it waits the amount of time it is configured for (just like a periodic timer does) and then executes the callback. However, the timer will not continue to do anything beyond this. No looping is done, nor any cleanup.

Timers of any type do *not* run in parallel. Although the actual marking of callbacks for run indeed is asynchronous, the callback itself is executed while evaluating the game’s regular mainloop (a consequence of this is that your callback won’t be called with 100% accuracy regarding the timespan, it will be cropped to the next frame). Therefore it is recommended to not put very time-consuming actions into a timer’s callback function as it will slow down the entire game. For example, you do not want to calculate π inside your timer’s callback function. Moving objects around on the other hand should be OK.

Note a particularity with objects of this class: Even when a timer goes out of scope, it doesn’t cease to exist (instead, the instances are remembered in an internal class-instance variable). So, if you create a periodic timer like this:

def create_timer
  timer = Timer.new(1000, true){puts "Hi there"}
  timer.start
end

create_timer

You might expect the periodic timer to stop working when the timer variable goes out of scope and the MRuby DATA it references gets garbage-collected, deallocating the C++ instance. This his however not the case; to prevent you from having to create a massive amount of global objects for your timers, a C++ timer, once started, will continue to work even after its MRuby counterpart has gone out of your reach. The bottomside of this is that you can’t influence the timer anymore after it has gone out of your scope, there is no way to get a reference to it again. The only thing you can do to force the C++ timer to stop is to end the level, which is probably not what you want :-).

Having that said, I want to enourage you to be brave and let your timers go out of scope. You do not have to call #stop manually when ending the level, this is done automatically for you. So no reason to clutter the global scope with timers.

Last but not least you shouldn’t use ::new directly. Use the ::after and ::every class methods instead, as they make your intention more clear and are more readable than a true or false passed to ::new and they also call #start automatically for you.

Timer.every(1000) do
  puts "Callback"
end

Class Methods

after

after( millisecs ){...} → a_timer

Shortcut for calling ::new with is_periodic = false followed by a call to #start.

Parameters

millisecs

The number of milliseconds to wait before the callback gets executed.

Return value

The newly created instance.

every

every( interval ){...} → a_timer

Shortcut for calling ::new with is_periodic = true followed by a call to #start.

Parameters

interval

The interval at which to fire the callback, in milliseconds.

Return value

The newly created instance.

new

new( interval [, is_periodic ] ){...} → a_timer

Creates a new Timer instance, either periodic or non-periodic depending on the last parameter’s value.

Parameters

interval

The timespan to configure the timer for, in milliseconds. With a periodic timer, this is the waiting time between calls to your callback function, with a non-periodic timer this is the time to wait before the one and only call to your callback.

is_periodic (false)

If this is a truth value, create a periodic (repeating) timer instead of a non-repeating (one-shot) timer.

Return value

The newly created instance.

Instance Methods

active?

active?()

Returns true if the timer is running, false otherwise. An already fired one-shot timer is considered stopped for this matter.

Also note this method is subject to a little race condition, because the timer ticks in a different C++ thread. It may be the case that the thread exits the very moment after you called this method, but before your next code instruction, so be careful. Probably you should only use this method while debugging.

inspect

inspect()

Human-readable description.

interval

interval() → an_integer

Returns the time interval for this timer, in milliseconds.

start

start()

Set the timer active.

stop

stop()

Stop the timer. This method blocks until the timer has stopped, which should be pretty quickly since the halting condition is checked several times a second.

Stopping the timer means that the callback associated with it will not be run. If you stop a ticking oneshot timer, this means it is never run at all.

Calling #stop on an executed oneshot timer is safe as it does nothing (except some internal cleanup).