1
0
Fork 0
mirror of https://github.com/Reuh/ubiquitousse.git synced 2025-10-27 09:09:30 +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

@ -99,7 +99,7 @@ let signal = {
-- For example, every ubiquitousse module with a "update" function will bind it to the "update" signal in the registry; -- For example, every ubiquitousse module with a "update" function will bind it to the "update" signal in the registry;
-- you can then call this signal on each game update to update every ubiquitousse module easily. -- you can then call this signal on each game update to update every ubiquitousse module easily.
-- Provided signals: -- Provided signals:
-- * update, should be called on every game update -- * update(dt), should be called on every game update
-- * draw, should be called on every game draw -- * draw, should be called on every game draw
-- * for LÖVE, there are callbacks for every LÖVE callback function that need to be called on their corresponding LÖVE callback -- * for LÖVE, there are callbacks for every LÖVE callback function that need to be called on their corresponding LÖVE callback
-- @impl mixed -- @impl mixed

View file

@ -1,6 +0,0 @@
local timer = require((...):match("^(.-%.)backend").."timer")
local ctr = require("ctr")
timer.get = ctr.time
return timer

View file

@ -1,7 +0,0 @@
local timer = require((...):match("^(.-%.)backend").."timer")
timer.get = function()
return love.timer.getTime() * 1000
end
return timer

View file

@ -1,14 +1 @@
local time return require((...)..".timer")
local p = ...
if love then
time = require(p..".backend.love")
elseif package.loaded["ctr"] then
time = require(p..".backend.ctrulua")
elseif package.loaded["libretro"] then
error("NYI")
else
error("no backend for ubiquitousse.timer")
end
return time

View file

@ -1,5 +1,4 @@
--- ubiquitousse.timer --- ubiquitousse.timer
-- Depends on a backend.
-- Optional dependencies: ubiquitousse.signal (to bind to update signal in signal.event) -- Optional dependencies: ubiquitousse.signal (to bind to update signal in signal.event)
local loaded, signal = pcall(require, (...):match("^(.-)timer").."signal") local loaded, signal = pcall(require, (...):match("^(.-)timer").."signal")
if not loaded then signal = nil end if not loaded then signal = nil end
@ -29,15 +28,9 @@ end
local registry_mt = { local registry_mt = {
--- Update all the TimedFunctions calls. --- Update all the TimedFunctions calls.
-- Should be called at every game update; called by ubiquitousse.update. -- 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 -- @impl ubiquitousse
update = function(self, dt) update = function(self, dt)
local currentTime = timer.get()
if not dt then
dt = currentTime - self.lastTime
self.lastTime = currentTime
end
self.dt = dt self.dt = dt
local done = {} -- functions done running local done = {} -- functions done running
@ -47,36 +40,50 @@ local registry_mt = {
if t and all(t.initWhen, true) then if t and all(t.initWhen, true) then
t.initWhen = {} t.initWhen = {}
local co = t.coroutine local co = t.coroutine
t.after = t.after - dt -- skip
if t.forceStart or (t.after <= 0 and all(t.startWhen, true)) then 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 = {} t.startWhen = {}
d[func] = false -- niling here cause the next pair iteration to error d[func] = false -- niling here cause the next pair iteration to error
table.insert(done, func) table.insert(done, func)
if not co then if not co then
co = coroutine.create(func) co = coroutine.create(func)
t.coroutine = co t.coroutine = co
t.started = currentTime
if t.times > 0 then t.times = t.times - 1 end 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 end
-- update
assert(coroutine.resume(co, function(delay) assert(coroutine.resume(co, function(delay)
t.after = delay or 0 t.after = delay - startLag
d[func] = t d[func] = t
coroutine.yield() coroutine.yield()
end, dt)) 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 all(t.stopWhen, false) then t.forceStop = true end
if t.forceStop or coroutine.status(co) == "dead" then if t.forceStop or coroutine.status(co) == "dead" then
if t.forceStop 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 (t.times == 0)
or (not all(t.repeatWhile, true)) 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 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 else
if t.times > 0 then t.times = t.times - 1 end 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) t.coroutine = coroutine.create(func)
d[func] = t d[func] = t
end end
@ -95,6 +102,7 @@ local registry_mt = {
--- Schedule a function to run. --- 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. -- 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 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 -- @tparam[opt] function func the function to schedule
-- @treturn TimedFunction the object -- @treturn TimedFunction the object
-- @impl ubiquitousse -- @impl ubiquitousse
@ -107,12 +115,11 @@ local registry_mt = {
self.delayed[func] = { self.delayed[func] = {
object = nil, object = nil,
coroutine = nil, coroutine = nil,
started = 0,
after = -1, after = nil,
every = -1, every = nil,
times = -1, times = -1,
during = -1, during = nil,
initWhen = {}, initWhen = {},
startWhen = {}, startWhen = {},
@ -121,6 +128,7 @@ local registry_mt = {
forceStart = false, forceStart = false,
forceStop = false, forceStop = false,
skip = nil,
onStart = {}, onStart = {},
onUpdate = {}, onUpdate = {},
@ -132,21 +140,25 @@ local registry_mt = {
r = { r = {
--- Timed conditions --- --- Timed conditions ---
--- Wait time milliseconds before running the function. --- Wait time milliseconds before running the function.
-- Specify no time to remove condition.
after = function(_, time) after = function(_, time)
t.after = time t.after = time
return r return r
end, end,
--- Run the function every time millisecond. --- Run the function every time millisecond.
-- Specify no time to remove condition.
every = function(_, time) every = function(_, time)
t.every = time t.every = time
return r return r
end, end,
--- The function will not execute more than count times. --- The function will not execute more than count times.
-- Specify no time to remove condition.
times = function(_, count) times = function(_, count)
t.times = count t.times = count or -1
return r return r
end, end,
--- The TimedFunction will be active for a time duration. --- The TimedFunction will be active for a time duration.
-- Specify no time to remove condition.
during = function(_, time) during = function(_, time)
t.during = time t.during = time
return r return r
@ -191,32 +203,40 @@ local registry_mt = {
t.forceStop = true t.forceStop = true
return r return r
end, end,
--- Skip some amount of time.
skip = function(_, time)
t.skip = (t.skip or 0) + time
end,
--- Callbacks functions --- --- Callbacks functions ---
--- Will execute func(self) when the function execution start. --- Will execute func(self, lag) when the function execution start.
onStart = function(_, func) onStart = function(_, func)
table.insert(t.onStart, func) table.insert(t.onStart, func)
return r return r
end, 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) onUpdate = function(_, func)
table.insert(t.onUpdate, func) table.insert(t.onUpdate, func)
return r return r
end, end,
--- Will execute func(self) when the function execution end. --- Will execute func(self, lag) when the function execution end.
onEnd = function(_, func) onEnd = function(_, func)
table.insert(t.onEnd, func) table.insert(t.onEnd, func)
return r return r
end, end,
--- Chaining --- --- 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. -- Returns the new TimedFunction.
chain = function(_, func) chain = function(_, func)
local done = false local done = false
r:onEnd(function() done = true end) local fn = self:run(func)
return self:run(func)
:initWhen(function() return done end) :initWhen(function() return done end)
r:onEnd(function(self, lag)
done = true
fn:skip(lag)
end)
return fn
end end
} }
t.object = r t.object = r
@ -284,9 +304,13 @@ local registry_mt = {
end end
local done = false local done = false
r:onEnd(function() done = true end) local fn = self:tween(duration_, tbl_, to_, method_)
return self:tween(duration_, tbl_, to_, method_)
:initWhen(function() return done end) :initWhen(function() return done end)
r:onEnd(function(self, lag)
done = true
fn:skip(lag)
end)
return fn
end end
return r return r
@ -314,20 +338,11 @@ timer = {
-- This table is for internal use and shouldn't be used from an external script. -- This table is for internal use and shouldn't be used from an external script.
delayed = {}, delayed = {},
-- Used to calculate the deltatime
lastTime = timer.get(),
--- Time since last timer update (miliseconds). --- Time since last timer update (miliseconds).
dt = 0 dt = 0
}, registry_mt) }, registry_mt)
end, 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). --- Time since last update (miliseconds).
-- @impl ubiquitousse -- @impl ubiquitousse
dt = 0, dt = 0,
@ -335,7 +350,6 @@ timer = {
--- Global TimerRegistry. --- Global TimerRegistry.
-- @impl ubiquitousse -- @impl ubiquitousse
delayed = {}, delayed = {},
lastTime = 0,
update = function(...) -- If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event. update = function(...) -- If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
return registry_mt.update(timer, ...) return registry_mt.update(timer, ...)
end, end,