Skip to content

Instantly share code, notes, and snippets.

@a327ex
Created January 19, 2021 17:49
Show Gist options
  • Save a327ex/68fb459fbed211dd4c6627a0a5233ee0 to your computer and use it in GitHub Desktop.
Save a327ex/68fb459fbed211dd4c6627a0a5233ee0 to your computer and use it in GitHub Desktop.
-- The base Timer class. Useful for juicing things up generally.
-- A global instance of this called "timer" is available by default.
Timer = Object:extend()
function Timer:new()
self.timers = {}
end
-- Calls the action every frame until it's cancelled via timer:cancel.
-- The tag must be passed in otherwise there will be no way to stop this from running.
-- If after is passed in then it is called after the run is cancelled.
function Timer:run(action, after, tag)
local tag = tag or random:uid()
local after = after or function() end
self.timers[tag] = {type = "run", after = after, action = action}
end
-- Calls the action after delay seconds.
-- Or calls the action after the condition is true.
-- If tag is passed in then any other timer actions with the same tag are automatically cancelled.
-- timer:after(2, function() print(1) end) -> prints 1 after 2 seconds
-- timer:after(function() return self.should_print_1 end, function() print(1) end) -> prints 1 after self.should_print_1 is set to true
function Timer:after(delay, action, tag)
local tag = tag or random:uid()
if type(delay) == "number" or type(delay) == "table" then
self.timers[tag] = {type = "after", timer = 0, unresolved_delay = delay, delay = self:resolve_delay(delay), action = action}
else
self.timers[tag] = {type = "conditional_after", condition = delay, action = action}
end
end
-- Calls the action every delay seconds.
-- Or calls the action once every time the condition becomes true.
-- If times is passed in then it only calls action for that amount of times.
-- If after is passed in then it is called after the last time action is called.
-- If tag is passed in then any other timer actions with the same tag are automatically cancelled.
-- timer:every(2, function() print(1) end) -> prints 1 every 2 seconds
-- timer:every(2, function() print(1) end, 5, function() print(2) end) -> prints 1 every 2 seconds 5 times, and then prints 2
function Timer:every(delay, action, times, after, tag)
local times = times or 0
local after = after or function() end
local tag = tag or random:uid()
if type(delay) == "number" or type(delay) == "table" then
self.timers[tag] = {type = "every", timer = 0, unresolved_delay = delay, delay = self:resolve_delay(delay), action = action, times = times, max_times = times, after = after}
else
self.timers[tag] = {type = "conditional_every", condition = delay, last_condition = false, action = action, times = times, max_times = times, after = after}
end
end
-- Same as every except the action is called immediately when this function is called, and then every delay seconds.
function Timer:every_immediate(delay, action, times, after, tag)
local times = times or 0
local after = after or function() end
local tag = tag or random:uid()
self.timers[tag] = {type = "every", timer = 0, unresolved_delay = delay, delay = self:resolve_delay(delay), action = action, times = times, max_times = times, after = after}
action()
end
-- Calls the action every frame for delay seconds.
-- Or calls the action every frame the condition is true.
-- If after is passed in then it is called after the duration ends or after the condition becomes false.
-- If tag is passed in then any other timer actions with the same tag are automatically cancelled.
-- timer:during(5, function() print(random:float(0, 100)) end)
-- timer:during(function() return self.should_print_random_float end, function() print(random:float(0, 100)) end) -> prints the random float as long as self.should_print_random_float is true
function Timer:during(delay, action, after, tag)
local after = after or function() end
local tag = tag or random:uid()
if type(delay) == "number" or type(delay) == "table" then
self.timers[tag] = {type = "during", timer = 0, unresolved_delay = delay, delay = self:resolve_delay(delay), action = action, after = after}
elseif type(delay) == "function" then
self.timers[tag] = {type = "conditional_during", condition = delay, last_condition = false, action = action, after = after}
end
end
-- Tweens the target's values specified by the source table for delay seconds using the given tweening method.
-- All tween methods can be found in the math/math file.
-- If after is passed in then it is called after the duration ends.
-- If tag is passed in then any other timer actions with the same tag are automatically cancelled.
-- timer:tween(0.2, self, {sx = 0, sy = 0}, math.linear) -> tweens this object's scale variables to 0 linearly over 0.2 seconds
-- timer:tween(0.2, self, {sx = 0, sy = 0}, math.linear, function() self.dead = true end) -> tweens this object's scale variables to 0 linearly over 0.2 seconds and then kills it
function Timer:tween(delay, target, source, method, after, tag)
local method = method or math.linear
local after = after or function() end
local tag = tag or random:uid()
local initial_values = {}
for k, _ in pairs(source) do initial_values[k] = target[k] end
self.timers[tag] = {type = "tween", timer = 0, unresolved_delay = delay, delay = self:resolve_delay(delay), target = target, initial_values = initial_values, source = source, method = method, after = after}
end
-- Cancels a timer action based on its tag.
-- This is automatically called if repeated tags are given to timer actions.
function Timer:cancel(tag)
if self.timers[tag].type == "run" then
self.timers[tag].after()
end
self.timers[tag] = nil
end
-- Returns the delay of a given tag.
-- This is useful when delays are set randomly (timer:every({2, 4}, ...) would set the delay at a random number between 2 and 4) and you need to know what the value chosen was.
function Timer:get_delay(tag)
return self.timers[tag].delay
end
-- Returns the current iteration of an every timer action with the given tag.
-- Useful if you need to know that its the nth time an every action has been called.
function Timer:get_every_iteration(tag)
return self.timers[tag].max_times - self.timers[tag].times
end
-- Returns the elapsed time of a given timer as a number between 0 and 1.
-- Useful if you need to know where you currently are in the duration of a during call.
function Timer:get_during_elapsed_time(tag)
return self.timers[tag].timer/self.timers[tag].delay
end
function Timer:resolve_delay(delay)
if type(delay) == "table" then
return random:float(delay[1], delay[2])
else
return delay
end
end
function Timer:update(dt)
for tag, timer in pairs(self.timers) do
timer.timer = timer.timer + dt
if timer.type == "run" then
timer.action()
elseif timer.type == "after" then
if timer.timer > timer.delay then
timer.action()
self.timers[tag] = nil
end
elseif timer.type == "conditional_after" then
if timer.condition() then
timer.action()
self.timers[tag] = nil
end
elseif timer.type == "every" then
if timer.timer > timer.delay then
timer.action()
timer.timer = timer.timer - timer.delay
timer.delay = self:resolve_delay(timer.unresolved_delay)
if timer.times > 0 then
timer.times = timer.times - 1
if timer.times <= 0 then
timer.after()
self.timers[tag] = nil
end
end
end
elseif timer.type == "conditional_every" then
local condition = timer.condition()
if condition and not timer.last_condition then
timer.action()
if timer.times > 0 then
timer.times = timer.times - 1
if timer.times <= 0 then
timer.after()
self.timers[tag] = nil
end
end
end
timer.last_condition = condition
elseif timer.type == "during" then
timer.action()
if timer.timer > timer.delay then
self.timers[tag] = nil
end
elseif timer.type == "conditional_during" then
local condition = timer.condition()
if condition then
timer.action()
end
if timer.last_condition and not condition then
timer.after()
end
timer.last_condition = condition
elseif timer.type == "tween" then
local t = timer.method(timer.timer/timer.delay)
for k, v in pairs(timer.source) do
timer.target[k] = math.lerp(t, timer.initial_values[k], v)
end
if timer.timer > timer.delay then
timer.after()
self.timers[tag] = nil
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment