1
0
Fork 0
mirror of https://github.com/Reuh/ubiquitousse.git synced 2025-10-27 17:19:31 +00:00

uqt.timer: now account for update lag, use only dt for calculations

This commit is contained in:
Étienne Fildadut 2019-12-29 22:32:30 +01:00
parent 6dc939bd16
commit f22e0bef26
5 changed files with 57 additions and 69 deletions

View file

@ -1,5 +1,4 @@
--- ubiquitousse.timer
-- Depends on a backend.
-- Optional dependencies: ubiquitousse.signal (to bind to update signal in signal.event)
local loaded, signal = pcall(require, (...):match("^(.-)timer").."signal")
if not loaded then signal = nil end
@ -29,15 +28,9 @@ end
local registry_mt = {
--- Update all the TimedFunctions calls.
-- Should be called at every game update; called by ubiquitousse.update.
-- @tparam[opt=calculate here] number dt the delta-time (time spent since last time the function was called) (miliseconds)
-- @tparam number dt the delta-time (time spent since last time the function was called) (miliseconds)
-- @impl ubiquitousse
update = function(self, dt)
local currentTime = timer.get()
if not dt then
dt = currentTime - self.lastTime
self.lastTime = currentTime
end
self.dt = dt
local done = {} -- functions done running
@ -47,36 +40,50 @@ local registry_mt = {
if t and all(t.initWhen, true) then
t.initWhen = {}
local co = t.coroutine
t.after = t.after - dt
if t.forceStart or (t.after <= 0 and all(t.startWhen, true)) then
-- skip
dt = self.dt
if t.skip then dt = dt + t.skip end
-- start
if t.after then t.after = t.after - dt end
if t.forceStart or ((not t.after or t.after <= 0) and all(t.startWhen, true)) then
local startLag = 0
if t.after then
startLag = -t.after
elseif t.skip then
startLag = t.skip
end
t.after, t.skip = nil, nil
t.startWhen = {}
d[func] = false -- niling here cause the next pair iteration to error
table.insert(done, func)
if not co then
co = coroutine.create(func)
t.coroutine = co
t.started = currentTime
if t.times > 0 then t.times = t.times - 1 end
for _, f in ipairs(t.onStart) do f(t.object) end
for _, f in ipairs(t.onStart) do f(t.object, startLag) end
end
-- update
assert(coroutine.resume(co, function(delay)
t.after = delay or 0
t.after = delay - startLag
d[func] = t
coroutine.yield()
end, dt))
for _, f in ipairs(t.onUpdate) do f(t.object) end
for _, f in ipairs(t.onUpdate) do f(t.object, startLag) end
if t.during then t.during = t.during - startLag - dt end
-- stopping / repeat
if all(t.stopWhen, false) then t.forceStop = true end
if t.forceStop or coroutine.status(co) == "dead" then
if t.forceStop
or (t.during >= 0 and t.started + t.during < currentTime)
or (t.during and t.during <= 0)
or (t.times == 0)
or (not all(t.repeatWhile, true))
or (t.every == -1 and t.times == -1 and t.during == -1 and #t.repeatWhile == 0) -- no repeat
or (t.every == nil and t.times == -1 and t.during == nil and #t.repeatWhile == 0) -- no repeat
then
for _, f in ipairs(t.onEnd) do f(t.object) end
local endLag = t.during and -t.during or 0
for _, f in ipairs(t.onEnd) do f(t.object, endLag) end
else
if t.times > 0 then t.times = t.times - 1 end
t.after = t.every
if t.every then t.after = t.every - startLag end
t.coroutine = coroutine.create(func)
d[func] = t
end
@ -95,6 +102,7 @@ local registry_mt = {
--- Schedule a function to run.
-- The function will receive as first parameter the wait(time) function, which will pause the function execution for time miliseconds.
-- As a second parameter, the function will receive the delta time (dt).
-- As a third parameter, the function will receive the lag time (difference between the time when the function was run and when it should have been run).
-- @tparam[opt] function func the function to schedule
-- @treturn TimedFunction the object
-- @impl ubiquitousse
@ -107,12 +115,11 @@ local registry_mt = {
self.delayed[func] = {
object = nil,
coroutine = nil,
started = 0,
after = -1,
every = -1,
after = nil,
every = nil,
times = -1,
during = -1,
during = nil,
initWhen = {},
startWhen = {},
@ -121,6 +128,7 @@ local registry_mt = {
forceStart = false,
forceStop = false,
skip = nil,
onStart = {},
onUpdate = {},
@ -132,21 +140,25 @@ local registry_mt = {
r = {
--- Timed conditions ---
--- Wait time milliseconds before running the function.
-- Specify no time to remove condition.
after = function(_, time)
t.after = time
return r
end,
--- Run the function every time millisecond.
-- Specify no time to remove condition.
every = function(_, time)
t.every = time
return r
end,
--- The function will not execute more than count times.
-- Specify no time to remove condition.
times = function(_, count)
t.times = count
t.times = count or -1
return r
end,
--- The TimedFunction will be active for a time duration.
-- Specify no time to remove condition.
during = function(_, time)
t.during = time
return r
@ -191,32 +203,40 @@ local registry_mt = {
t.forceStop = true
return r
end,
--- Skip some amount of time.
skip = function(_, time)
t.skip = (t.skip or 0) + time
end,
--- Callbacks functions ---
--- Will execute func(self) when the function execution start.
--- Will execute func(self, lag) when the function execution start.
onStart = function(_, func)
table.insert(t.onStart, func)
return r
end,
--- Will execute func(self) each frame the main function is run.
--- Will execute func(self, lag) each frame the main function is run.
onUpdate = function(_, func)
table.insert(t.onUpdate, func)
return r
end,
--- Will execute func(self) when the function execution end.
--- Will execute func(self, lag) when the function execution end.
onEnd = function(_, func)
table.insert(t.onEnd, func)
return r
end,
--- Chaining ---
--- Creates another TimedFunction which will be initialized when the current one ends.
--- Creates another TimedFunction which will be initialized immediately when the current one ends.
-- Returns the new TimedFunction.
chain = function(_, func)
local done = false
r:onEnd(function() done = true end)
return self:run(func)
local fn = self:run(func)
:initWhen(function() return done end)
r:onEnd(function(self, lag)
done = true
fn:skip(lag)
end)
return fn
end
}
t.object = r
@ -284,9 +304,13 @@ local registry_mt = {
end
local done = false
r:onEnd(function() done = true end)
return self:tween(duration_, tbl_, to_, method_)
local fn = self:tween(duration_, tbl_, to_, method_)
:initWhen(function() return done end)
r:onEnd(function(self, lag)
done = true
fn:skip(lag)
end)
return fn
end
return r
@ -314,20 +338,11 @@ timer = {
-- This table is for internal use and shouldn't be used from an external script.
delayed = {},
-- Used to calculate the deltatime
lastTime = timer.get(),
--- Time since last timer update (miliseconds).
dt = 0
}, registry_mt)
end,
--- Returns the number of miliseconds elapsed since some point in time.
-- This point is fixed but undetermined, so this function should only be used to calculate durations.
-- Should at least have millisecond-precision, but can be more precise if available.
-- @impl backend
get = function() end,
--- Time since last update (miliseconds).
-- @impl ubiquitousse
dt = 0,
@ -335,7 +350,6 @@ timer = {
--- Global TimerRegistry.
-- @impl ubiquitousse
delayed = {},
lastTime = 0,
update = function(...) -- If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
return registry_mt.update(timer, ...)
end,