diff --git a/backend/love.lua b/backend/love.lua index bbfb432..454c587 100644 --- a/backend/love.lua +++ b/backend/love.lua @@ -42,15 +42,6 @@ end -- abstract abstract.backend = "love" -add(abstract, { - setup = function(params) - local p = abstract.params - love.window.setTitle(p.title) - love.window.setMode(p.width, p.height, { - resizable = p.resizable - }) - end -}) -- abstract.event do @@ -58,8 +49,7 @@ local updateDefault = abstract.event.update abstract.event.update = function() end function love.update(dt) -- Value update - abstract.fps = love.timer.getFPS() - abstract.dt = love.timer.getDelta() + abstract.draw.fps = love.timer.getFPS() -- Stuff defined in abstract.lua updateDefault(dt) @@ -95,6 +85,13 @@ end -- abstract.draw local defaultFont = love.graphics.getFont() add(abstract.draw, { + init = function(params) + local p = abstract.draw.params + love.window.setTitle(p.title) + love.window.setMode(p.width, p.height, { + resizable = p.resizable + }) + end, color = function(r, g, b, a) love.graphics.setColor(r, g, b, a) end, diff --git a/draw.lua b/draw.lua index 1745a8d..098fd2a 100644 --- a/draw.lua +++ b/draw.lua @@ -11,7 +11,45 @@ local abstract = require((...):match("^(.-abstract)%.")) -- -- x and y values can be float, so make sure to perform math.floor if your engine only support -- integer coordinates. -return { +local draw +draw = { + --- Initial game view paramters (some defaults). + -- @impl abstract + params = { + title = "Abstract Engine", + width = 800, + height = 600, + resizable = false, + resizeType = "auto" + }, + + --- Setup the intial game view parameters. + -- If a parmeter is not set, a default value will be used. + -- This function is expected to be only called once, before doing any drawing operation. + -- @tparam table params the game parameters + -- @usage -- Default values: + -- abstract.setup { + -- title = "Abstract Engine", -- usually window title + -- width = 800, -- in px + -- height = 600, -- in px + -- resizable = false, -- can the game be resized? + -- resizeType = "auto" -- how to act on resize: "none" to do nothing (0,0 will be top-left) + -- "center" to autocenter (0,0 will be at windowWidth/2-gameWidth/2,windowHeight/2-gameHeight/2) + -- "auto" to automatically resize to the window size (coordinate system won't change) + -- } + -- @impl mixed + init = function(params) + for k, v in pairs(params) do + draw.params[k] = v + end + draw.width = params.width + draw.height = params.height + end, + + --- Frames per second (the backend should update this value). + -- @impl backend + fps = 60, + --- Sets the drawing color -- @tparam number r the red component (0-255) -- @tparam number g the green component (0-255) @@ -55,11 +93,11 @@ return { --- The drawing area width, in pixels. -- @impl backend - width = abstract.params.width, + width = 800, --- The drawing area height, in pixels. -- @impl backend - height = abstract.params.height, + height = 600, -- TODO: doc & api push = function() end, @@ -70,3 +108,5 @@ return { font = function(filename) end, image = function(filename) end, } + +return draw diff --git a/init.lua b/init.lua index a9ead65..ccac91b 100644 --- a/init.lua +++ b/init.lua @@ -6,6 +6,9 @@ -- It is as the name imply abstract, and must be implemented in a backend, such as abstract.love. -- When required, this file will try to autodetect the engine it is running on, and load a correct backend. -- +-- abstract may or may not be used as a full game engine. You can delete the modules files you don't need and abstract +-- should adapt accordingly. +-- -- For backend writers: -- If a function defined here already contains some code, this means this code is mandatory and you must put/call -- it in your implementation. @@ -53,60 +56,17 @@ abstract = { --- Backend name. -- For consistency, only use lowercase letters [a-z] (no special char) -- @impl backend - backend = "unknown", - - --- General game paramters (some defaults). - -- @impl abstract - params = { - title = "Abstract Engine", - width = 800, - height = 600, - resizable = false, - resizeType = "auto" - }, - - --- Setup general game parameters. - -- If a parmeter is not set, a default value will be used. - -- This function is expected to be only called once, before doing any drawing operation. - -- @tparam table params the game parameters - -- @usage -- Default values: - -- abstract.setup { - -- title = "Abstract Engine", -- usually window title - -- width = 800, -- in px - -- height = 600, -- in px - -- resizable = false, -- can the game be resized? - -- resizeType = "auto" -- how to act on resize: "none" to do nothing (0,0 will be top-left) - -- "center" to autocenter (0,0 will be at windowWidth/2-gameWidth/2,windowHeight/2-gameHeight/2) - -- "auto" to automatically resize to the window size (coordinate system won't change) - -- } - -- @impl mixed - setup = function(params) - for k, v in pairs(params) do - abstract.params[k] = v - end - abstract.draw.width = params.width - abstract.draw.height = params.height - end, - - --- Frames per second (the backend should update this value). - -- @impl backend - fps = 60, - - --- Time since last frame (seconds) - -- @impl backend - dt = 0 + backend = "unknown" } -- We're going to require modules requiring abstract, so to avoid stack overflows we already register the abstract package package.loaded[p] = abstract --- External submodules -abstract.time = require(p..".time") -abstract.draw = require(p..".draw") -abstract.audio = require(p..".audio") -abstract.input = require(p..".input") -abstract.scene = require(p..".scene") -abstract.event = require(p..".event") +-- Require external submodules +for _, m in ipairs({"time", "draw", "audio", "input", "scene", "event"}) do + local s, t = pcall(require, p.."."..m) + if s then abstract[m] = t end +end -- Backend engine autodetect and load if love then diff --git a/input.lua b/input.lua index dc5890c..c2254a5 100644 --- a/input.lua +++ b/input.lua @@ -251,11 +251,11 @@ input = { if mode == "relative" then local movX, movY = math.abs(xAxis:value()), math.abs(yAxis:value()) if movX > maxMovX then - newX = x + (xSpeed and (xAxis:value() * xSpeed * abstract.dt) or xAxis:raw()) + newX = x + (xSpeed and (xAxis:value() * xSpeed * abstract.time.dt) or xAxis:raw()) maxMovX = movX end if movY > maxMovY then - newY = y + (ySpeed and (yAxis:value() * ySpeed * abstract.dt) or yAxis:raw()) + newY = y + (ySpeed and (yAxis:value() * ySpeed * abstract.time.dt) or yAxis:raw()) maxMovY = movY end elseif mode == "absolute" then diff --git a/time.lua b/time.lua index b3812c8..970b5e9 100644 --- a/time.lua +++ b/time.lua @@ -2,20 +2,25 @@ local ease = require((...):match("^(.-abstract)%.")..".lib.easing") --- Time related functions -local time local function newTimerRegistry() --- Used to store all the functions delayed with abstract.time.delay -- The default implementation use the structure { = , ...} -- This table is for internal use and shouldn't be used from an external script. local delayed = {} - return { + -- Used to calculate the deltatime + local lastTime + + local registry + registry = { --- Creates and return a new TimerRegistry. -- A TimerRegistry is a separate abstract.time instance: its TimedFunctions will be independant -- from the one registered using abstract.time.run (the global TimerRegistry). If you use the scene -- system, a scene-specific TimerRegistry is available at abstract.scene.current.time. new = function() - return newTimerRegistry() + local new = newTimerRegistry() + new.get = registry.get + return new end, --- Returns the number of seconds elapsed since some point in time. @@ -27,9 +32,21 @@ local function newTimerRegistry() --- Update all the TimedFunctions calls. -- Supposed to be called in abstract.event.update. - -- @tparam numder dt the delta-time + -- @tparam[opt=calculate here] numder dt the delta-time (time spent since last time the function was called) (seconds) -- @impl abstract update = function(dt) + if dt then + registry.dt = dt + else + if lastTime then + local newTime = registry.get() + registry.dt = newTime - lastTime + lastTime = newTime + else + lastTime = registry.get() + end + end + local d = delayed for func, t in pairs(d) do local co = t.coroutine @@ -39,7 +56,7 @@ local function newTimerRegistry() if not co then co = coroutine.create(func) t.coroutine = co - t.started = time.get() + t.started = registry.get() if t.times > 0 then t.times = t.times - 1 end t.onStart() end @@ -49,7 +66,7 @@ local function newTimerRegistry() coroutine.yield() end, dt)) if coroutine.status(co) == "dead" then - if (t.during >= 0 and t.started + t.during < time.get()) + if (t.during >= 0 and t.started + t.during < registry.get()) or (t.times == 0) or (t.every == -1 and t.times == -1 and t.during == -1) -- no repeat then @@ -138,7 +155,7 @@ local function newTimerRegistry() local from = {} for k in pairs(to) do from[k] = tbl[k] end - return time.run(function(wait, dt) + return registry.run(function(wait, dt) time = time + dt for k, v in pairs(to) do tbl[k] = method(time, from[k], to[k] - from[k], duration) @@ -150,9 +167,14 @@ local function newTimerRegistry() -- @impl abstract clear = function() delayed = {} - end + end, + + --- Time since last update (seconds). + -- @impl abstract + dt = 0 } + + return registry end -time = newTimerRegistry() -return time +return newTimerRegistry()