britzl’s timer library is great for a os time based timer.
Inspired by it, and needing a similar solution for handling events in my game but based on passed in game time rather than os time, I wrote a simple Lua module for it.
timer.lua
:
local M = {}
local callbacks = {}
local currentId = 0
local function buildCallback(func, seconds, ...)
currentId = currentId + 1
return {
instance = __dm_script_instance__,
id = currentId,
callback = func,
time = seconds,
timeLeft = seconds,
repeating = false,
paused = false,
args = { n = select("#", ...), ... }
}
end
function M.once(func, seconds, ...)
local callback = buildCallback(func, seconds, ...)
table.insert(callbacks, callback)
return currentId
end
function M.repeating(func, seconds, ...)
local callback = buildCallback(func, seconds, ...)
callback.repeating = true
table.insert(callbacks, callback)
return currentId
end
function M.cancel(id)
for i = 1, #callbacks do
if callbacks[i].id == id then
table.remove(callbacks, i)
return true
end
end
return false
end
function M.pause(id)
for i = 1, #callbacks do
if callbacks[i].id == id then
callbacks[i].paused = true
return true
end
end
return false
end
function M.unpause(id)
for i = 1, #callbacks do
if callbacks[i].id == id then
callbacks[i].paused = false
return true
end
end
return false
end
function M.cancel_all()
callbacks = {}
end
function M.update(dt)
for i = #callbacks, 1, -1 do
local callback = callbacks[i]
if callback.paused == false then
callback.timeLeft = callback.timeLeft - dt
if callback.timeLeft <= 0 then
if callback.repeating == true then
callback.timeLeft = callback.time
else
table.remove(callbacks, i)
end
if pcall( function() local test = callback.instance.property end ) then
local currentInstance = __dm_script_instance__
__dm_script_instance__ = callback.instance
callback.callback(unpack(callback.args, 1, callback.args.n))
__dm_script_instance__ = currentInstance
end
end
end
end
end
return M
How to use it? First, make sure you set up a game object dedicated to updating the timer, like so:
local timer = require "path/to/your/modules/timer"
function update(self, dt)
timer.update(dt)
end
And finally, how to actually add your timed functions:
-- simple call, supply your callback and seconds until trigger
timer.once(function()
print("Hello world!")
end, 0.5)
-- third/rest parameters turn into arguments for the callback
timer.once(function(name)
print("Oh hai " .. name .. "!")
end, 0.8, "Mark")
-- example of using more than one argument for the callback
timer.once(function(who, data)
print("It's " .. data.state .. ", I did not " .. data.action .. " " .. who)
end, 1, "her", { action = "hit", state = "not true"} )
-- and id is returned, can be used to cancel
local timerId = timer.once(function(name)
print("This will never trigger since we cancel it")
end, 2)
timer.cancel(timerId)
-- how to have a repeating callback instead
local repeatingId = timer.repeating(function(state)
state.count = state.count + 1
print("Repeating, count: " .. state.count)
end, 1, {count = 0} )
-- better cancel that repeater if we don't want it to run forever!
timer.once(function(id)
if timer.cancel(repeatingId) then
print("Cancelled the repeater")
end
end, 4, repeatingId)
Result:
DEBUG:SCRIPT: Hello world!
DEBUG:SCRIPT: Oh hai Mark!
DEBUG:SCRIPT: Repeating, count: 1
DEBUG:SCRIPT: It's not true, I did not hit her
DEBUG:SCRIPT: Repeating, count: 2
DEBUG:SCRIPT: Repeating, count: 3
DEBUG:SCRIPT: Cancelled the repeater
I’m not an expert on Lua, so if anyone more experienced want to vet the code please do not hesitate to call on improvements!
I haven’t gotten it working properly with functions referring to self
yet, perhaps someone can give some input on that?
For example, if I have a callback that calls go.get_id(), it returns hash: [/timer]
. So it’s in the context of the object calling update.
Is there way to call a function in the context of a specific game object?