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:
		
							parent
							
								
									6dc939bd16
								
							
						
					
					
						commit
						f22e0bef26
					
				
					 5 changed files with 57 additions and 69 deletions
				
			
		|  | @ -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; | ||||
| 	-- you can then call this signal on each game update to update every ubiquitousse module easily. | ||||
| 	-- 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 | ||||
| 	-- * 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 | ||||
|  |  | |||
|  | @ -1,6 +0,0 @@ | |||
| local timer = require((...):match("^(.-%.)backend").."timer") | ||||
| local ctr = require("ctr") | ||||
| 
 | ||||
| timer.get = ctr.time | ||||
| 
 | ||||
| return timer | ||||
|  | @ -1,7 +0,0 @@ | |||
| local timer = require((...):match("^(.-%.)backend").."timer") | ||||
| 
 | ||||
| timer.get = function() | ||||
| 	return love.timer.getTime() * 1000 | ||||
| end | ||||
| 
 | ||||
| return timer | ||||
|  | @ -1,14 +1 @@ | |||
| local time | ||||
| 
 | ||||
| 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 | ||||
| return require((...)..".timer") | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue