< Execution of scripts | UserGuide | Garbage collection >


Timers and Functioncallbacks

After the first execution of the main.lua file, Luxinia will call a certain function for each frame drawn. The API that we provide is using this function to implement a timer system in order to call functions that can then control the user input or any other type of execution that needs to be done to run a project. I.e. the gamelogic is normaly a function that is called on a regular basis.

Time

First, it should be clear what time means and what problems it makes. Most games are containing animated objects such as cars and so on. To animate them properly, we need to update the positions and rotations each frame that is drawn, or at least often enough to make the user believe that a continous movement is shown here. So let's make some math. A point in space can be described as a vector, like (0,1,0). If we know, that this point is moving with a certain velocity, i.e. one meter per second towards positive x, we can describe the velocity as a vector: (1,0,0). If we want to know, where the point will be in 5 seconds, we only need to update the position to p = p + v * t, where p is (0,1,0), v is (1,0,0) and t is 5, which will result in (5,1,0). If we draw now a frame, we only have to determine the time that has passed and update the point's position.

However, the case is not always, or even worse, most of the time not as simple as a linear movement. Once we are using other formulars that will describe various parameters of a movement such as drag or acceleration, calculating the position won't be such easy any longer. In fact, it gets very complicated and error prone, depending on the size of the timestep. So, the old known problem arises again, that a game running on different fast computers will result in different animations. This is certainly not what we want. There are two ways to approach this problem: Know math very well and find a formular that will take all your calculations in account and can tell you the exact position for any point of time. But this is anything but easy - it is just as if you looked out the window, seing a car that is parking on the right side of the street. Then you go away for five minutes and the car is stainding on the left side of the street. Just knowing both locations, you cannot know how the car drove while you didn't watch.

The other approach is to do small well defined timesteps. In example, you can decide to run your code to update the positions in your game at a regular time interval, for example 60 times per second. In that case, we need to make sure, that if the function was not called often enough during the last frame, we need to catch up the lost executions by doing them both in the current frame. If the computer is not able to catch up, it will slow down. In that case, we can do nothing about it than optimizing the update functions.

Callback system in Luxinia

There are two systems that allow to call a function on a regular basis and which is used by other parts like keyboard-, mouse- or guihandling: The timers and the timertask.

A timer is a function that is called regularry within a certain time interval. Lets say we have a function called gamelogic and we want it to execute it 50 times per second and we donnot want to try to catch up more than 5 update calls:

Timer.set("the gamelogic",gamelogic,20,5)

This will install a timer callback that is called every 20 milliseconds. If we don't need a timer any longer, we can remove it:

Timer.remove("the gamelogic")
 -- or
Timer.remove(gamelogic)
Whenever an error is thrown in a timer function, the error will be displayed on the console and the timer is disabled - otherwise it might happen that an error is printed every time the function is executed - and the first error (which might cause other errors) is not shown anymore since the console is flooded.

A typical error
A typical error message on the console

The other system is provided by a class named TimerTask?. Timertasks are functions that are called at a certain point of execution. The Task is then done and once finished, it won't be called again. However, tasks can yield at any time of execution, returning a value when they should be continued again.

---% Code tested with Luxinia 0.95 build Nov 15 2006
---% at 11/16/06 23:15:45
---%  bugged in 0.94 due to error in timer.lua

local function fire ()
  for i=1,9 do
    print("firering in ",i,"seconds")

    coroutine.yield(1000) -- continue in a second
  end

  for i=10,2,-1 do
    print(i*100,"ms left!")
    coroutine.yield(100) -- continue in 100 millisecs.
  end

  print "BOOM"
end

TimerTask.new(fire,1000) -- start countdown in a second