/chrono

Timer module for LÖVE

Primary LanguageLua

chrono

chrono is a timer module for LÖVE. It's a copy of hump.timer with a few improvements on top.


Contents


Usage

Timer = require 'Timer'

Creating a timer object

function love.load()
  timer = Timer()
end

function love.update(dt)
  timer:update(dt)
end

All created timers must be updated otherwise they won't work. You can create multiple timers and personally I create one for each entity that needs timing functions. However, you can also only use a single global one if you think that's better.


after

function love.load()
    ...
    timer:after(2, function() print(1) end)
end

The after function will execute an action after a given amount of time. It receives a number and a function, representing the delay and the action respectively.

These are all the valid ways in which after can be called:

timer:after(delay, action)
timer:after(delay, action, tag)

every

function love.load()
    ...
    timer:every(0.5, function() print(1) end)
end

The every function will execute an action repeatedly at an interval. It receives a number and a function, representing the interval duration and the action respectively. A third count argument can be passed in to limit the amount of times the action will be run:

function love.load()
    ...
    timer:every(0.5, function() print(1) end, 5)
end

The example above will print 1 a total of 5 times with a 0.5 seconds interval between each. This count argument is optional and can be omitted, and if it is then the timer will repeat forever or until it's cancelled.

A fourth after argument can be passed in as an action that will be executed once the every timer ends. This argument may only be passed in if count is also passed in.

function love.load()
    ...
    timer:every(0.5, function() print(1) end, 5, function() print(2) end)
end

The example above will print 1 a total of 5 times, and when that ends 2 will also be printed.

These are all the valid ways in which every can be called:

timer:every(delay, action)
timer:every(delay, action, tag)
timer:every(delay, action, count)
timer:every(delay, action, count, tag)
timer:every(delay, action, count, after)
timer:every(delay, action, count, after, tag)

during

function love.load()
    ...
    timer:during(1, function() print(1) end)
end

The during function will execute an action every frame for a given amount of time. It receives a number and a function, representing the duration and the action respectively. Additional, a third after argument can be passed in to be executed after the specified duration:

function love.load()
    ...
    timer:during(1, function() print(1) end, function() print(2) end)
end

The example above will print 1 every frame for 1 second, and then print 2 once at the end of that 1 second.

These are all the valid ways in which during can be called:

timer:during(delay, action)
timer:during(delay, action, tag)
timer:during(delay, action, after)
timer:during(delay, action, after, tag)

script

function love.load()
    ...
    timer:script(function(wait)
        print(1)
        wait(1)
        print(2)
        wait(1)
        print(3)
    end)
end

The script function will execute the statements inside the function passed in with the ability to stop the execution using the wait function. The code above is equivalent to a chain of afters:

function love.load()
    ...
    print(1)
    timer:after(1, function()
        print(2)
        timer:after(1, function()
            print(3)
        end)
    end)
end

tween

function love.load()
    ...
    player = {x = 100, y = 100}
    timer:tween(1, player, {x = 200, y = 200}, 'in-out-cubic', function() print('Player tween finished!') end)
end

The tween function will tween values from the subject table to the values in the target table over a certain duration using a certain tween method. In the example above, the x and y values in the player table will be changed from 100 to 200 over 1 second via the 'in-out-cubic' tween method, and then after the tween is done 'Player tween finished!' will be printed to the console. A list of all tween modes available as well as further explanation can be found here.

These are all the valid ways in which tween can be called:

timer:tween(delay, subject, target, method)
timer:tween(delay, subject, target, method, tag)
timer:tween(delay, subject, target, method, after)
timer:tween(delay, subject, target, method, after, tag)

Tags

This library has the same set of functions that hump.timer has: after, every, during, tween and script. The main difference is that all functions (except for script) can accept a tag argument. This argument takes care of a very common pattern that happens with timers which is that a timer is usually started whenever an event happens. The problem with this is that generally we have no control over how often this event happens, which means often we get overlapping timers. Consider the following example:

function love.keypressed(key)
    if key == 'k' then
        timer:after(2, function() print(math.random()) end)
    end
end

In this example whenever we press 'k', a random number will be printed to the console 2 seconds later. If we press 'k' repeatedly really fast then we'll get random numbers printed multiple times, always 2 seconds after whenever we pressed 'k'. However, often times what we want in our gameplay is that instead of performing the action multiple times, if the timer is already running for this same action, we want to cancel the currently running one and start the timer over. The behavior then would be that if 'k' is pressed repeatedly really fast, we'd only see a random number printed to the console 2 seconds after the last press.

To achieve this behavior the library makes use of tags. Tags are available for all functions (except script) and they're always the last argument. If no tag is given then the default behavior happens (the one explained above), otherwise we get the resetting behavior that we often want. For the after function in the example above it would look like this:

function love.keypressed(key)
    if key == 'k' then
        timer:after(2, function() print(math.random()) end, 'random_number')
    end
end

And so if we press 'k' multiple times really fast, we'll only see a random number printed 2 seconds after the last press. Tags should always be strings and they should always be unique within each timer. If you have two different actions that share the same tag then they'll cancel each other out. For instance:

function love.keypressed(key)
    if key == 'k' then
        timer:after(2, function() print(math.random()) end, 'random_number')
    end
    
    if key == 'l' then
        timer:after(2, function() print(1) end, 'random_number')
    end
end

In the example above, pressing either 'k' or 'l' will reset the 'random_number' timer, which means that we'll only get either a random number or 1 printed whenever we let 2 seconds pass after the pressing either key. Usually in gameplay code we want different actions to have different tags, since generally we want to cancel timers only between multiple calls of the same action.


Random delays

All functions in the library also accept random delays. By default, the delay argument of each function should be a number, but it can also be a table containing 2 numbers that serve as an interval.

function love.load()
    ...
    timer:after({1, 2}, function() print(math.random()) end)
end

And so in the example above a random number will be printed after a random amount of time between 1 and 2 seconds. This behavior is especially useful with the every function:

function love.load()
    ...
    timer:every({0.5, 1}, function() print(math.random()) end)
end

Here a random number will be printed at an interval that is a random amount of time between 0.5 and 1 second. So at first a number may be printed after 0.6 seconds, and then another 0.95 seconds after that, and another 0.76 seconds after that, and so on.


cancel, getTime

All main functions in the library return a tag:

tag = timer:after(1, function() print(1) end)

If the tag argument is specified then the returned value will be that, otherwise it will be a randomly created unique one. This tag can be used in conjunction with two other functions: cancel and getTime.

The cancel function will forcefully cancel the further execution of a given timer. For instance:

function love.load()
    ...
    tag = timer:after(1, function() print(1) end)
    timer:cancel(tag)
end

After the code above is run 1 will never be printed to the screen, since the timer that was responsible for executing that function was cancelled by the cancel function.

The getTime function will return the current and maximum values of this timer. For instance:

function love.load()
    ...
    tag = timer:after(1, function() print(1) end)
end

function love.keypressed(key)
    if key == 'k' then
        local current, maximum = timer:getTime(tag)
        print(current, maximum)
    end
end

And so whenever 'k' is pressed, the current and maximum values will be printed. The current value represents the current state of the timer. For instance, if we press 'k' 0.5 seconds after the game started then the current value will be 0.5. The maximum value is always the value that represents the end of the timer, so in this case it will be 1. For timers that use random delays it can be useful to figure out which was the random value chosen by the library via the maximum value.


destroy

timer:destroy()

The destroy function will clear the timer entirely. If you're using one timer per entity then it's probably necessary to call this whenever the entity dies otherwise memory may leak.


LICENSE

You can do whatever you want with this. See the license at the top of the main file.