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

Button combinations, create signal registry per scene

This commit is contained in:
Étienne Fildadut 2019-12-28 16:13:52 +01:00
parent f6fb8ad649
commit 15dfb18c65
3 changed files with 102 additions and 78 deletions

View file

@ -1,7 +1,7 @@
--- ubiquitousse.ecs
-- Optional dependency: ubiquitousse.scene, to allow quick creation of ECS-based scenes.
local loaded, newScene = pcall(require, (...):match("^(.-)ecs").."scene")
if not loaded then newScene = nil end
local loaded, scene = pcall(require, (...):match("^(.-)ecs").."scene")
if not loaded then scene = nil end
--- Entity Component System library, inspired by the excellent tiny-ecs. Main differences include:
-- * ability to nest systems;
@ -268,70 +268,72 @@ let recCallOnAddToWorld = (world, systems)
end
end
--- Create and returns a world system based on a list of systems.
-- The systems will be instancied for this world.
let world = (...)
let world = setmetatable({
filter = (e) return true end,
s = {}
}, { __index = system_mt })
world.world = world
world.systems = recInstanciateSystems(world, {...})
recCallOnAddToWorld(world, world.systems)
return world
end
--- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity.
let all = (...)
let l = {...}
return function(s, e)
for _, k in ipairs(l) do
if e[k] == nil then
return false
end
end
return true
end
end
--- Returns a filter that returns true if one of the arguments if the name of a field in the entity.
let any = (...)
let l = {...}
return function(s, e)
for _, k in ipairs(l) do
if e[k] ~= nil then
return true
end
end
return false
end
end
let scene = (name, systems={}, entities={})
let s = newScene(name)
let w
function s:enter()
w = world(unpack(systems))
w:add(unpack(entities))
end
function s:exit()
w:destroy()
end
function s:update(dt)
w:update(dt)
end
function s:draw()
w:draw()
end
return s
end
--- ECS module.
return {
world = world,
all = all,
any = any,
scene = scene
let ecs = {
--- Create and returns a world system based on a list of systems.
-- The systems will be instancied for this world.
-- @impl ubiquitousse
world = (...)
let world = setmetatable({
filter = (e) return true end,
s = {}
}, { __index = system_mt })
world.world = world
world.systems = recInstanciateSystems(world, {...})
recCallOnAddToWorld(world, world.systems)
return world
end,
--- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity.
-- @impl ubiquitousse
all = (...)
let l = {...}
return function(s, e)
for _, k in ipairs(l) do
if e[k] == nil then
return false
end
end
return true
end
end,
--- Returns a filter that returns true if one of the arguments if the name of a field in the entity.
-- @impl ubiquitousse
any = (...)
let l = {...}
return function(s, e)
for _, k in ipairs(l) do
if e[k] ~= nil then
return true
end
end
return false
end
end,
--- If uqt.scene is available, returns a new scene that will consist of a ECS world with the specified systems and entities.
-- @impl ubiquitousse
scene = (name, systems={}, entities={})
let s = scene.new(name)
let w
function s:enter()
w = ecs.world(unpack(systems))
w:add(unpack(entities))
end
function s:exit()
w:destroy()
end
function s:update(dt)
w:update(dt)
end
function s:draw()
w:draw()
end
return s
end
}
return ecs

View file

@ -501,6 +501,7 @@ input = {
-- identifier, preceded by a % : for example "gamepad.axis.1.leftx%-0.5" should return true when the left-stick of the first gamepad is moved to the right
-- by more of 50%. The negative threshold value means that the button will be pressed only when the axis has a negative value (in the example, it won't be
-- pressed when the axis is moved to the right).
-- Buttons can also be defined by a list of buttons (string or functions), in which case the button will be considered down if all the buttons are down.
--- Makes a new button detector from a identifier string.
-- The function may error if the identifier is incorrect.
@ -509,7 +510,7 @@ input = {
-- @impl backend
basicButtonDetector = function(str) end,
--- Make a new button detector from a detector function of string.
--- Make a new button detector from a detector function, string, or list of buttons.
-- @tparam string, function button identifier
-- @impl ubiquitousse
buttonDetector = function(obj)
@ -517,6 +518,19 @@ input = {
return obj
elseif type(obj) == "string" then
return input.basicButtonDetector(obj)
elseif type(obj) == "table" then
local l = {}
for _, b in ipairs(obj) do
table.insert(l, input.buttonDetector(b))
end
return function()
for _, b in ipairs(l) do
if not b() then
return false
end
end
return true
end
end
error(("Not a valid button detector: %s"):format(obj))
end,

View file

@ -26,7 +26,7 @@ if not loaded then timer = nil end
-- * all scene change callbacks are called after setting scene.current to the new scene but before changing scene.stack
-- * all scene exit/suspend callbacks are called before scene enter/resume callbacks
local scene
scene = setmetatable({
scene = {
--- The current scene table.
-- @impl ubiquitousse
current = nil,
@ -34,6 +34,9 @@ scene = setmetatable({
--- Shortcut for scene.current.timer.
-- @impl ubiquitousse
timer = nil,
--- Shortcut for scene.current.signal.
-- @impl ubiquitousse
signal = nil,
--- The scene stack: list of scene, from the farest one to the nearest.
-- @impl ubiquitousse
@ -77,6 +80,7 @@ scene = setmetatable({
name = name or "unamed", -- The scene name.
timer = timer and timer.new(), -- Scene-specific TimerRegistry, if uqt.time is available.
signal = signal and signal.new(), -- Scene-specific SignalRegistry, if uqt.signal is available.
enter = function(self, ...) end, -- Called when entering a scene.
exit = function(self) end, -- Called when exiting a scene, and not expecting to come back (scene may be unloaded).
@ -101,8 +105,12 @@ scene = setmetatable({
local previous = scene.current
scene.current = type(scenePath) == "string" and scene.load(scene.prefix..scenePath) or scenePath
scene.timer = scene.current.timer
scene.signal = scene.current.signal
scene.current.name = scene.current.name or tostring(scenePath)
if previous then previous:exit() end
if previous then
previous:exit()
if timer then previous.timer:clear() end
end
scene.current:enter(...)
scene.stack[math.max(#scene.stack, 1)] = scene.current
end,
@ -118,6 +126,7 @@ scene = setmetatable({
local previous = scene.current
scene.current = type(scenePath) == "string" and scene.load(scene.prefix..scenePath) or scenePath
scene.timer = scene.current.timer
scene.signal = scene.current.signal
scene.current.name = scene.current.name or tostring(scenePath)
if previous then previous:suspend() end
scene.current:enter(...)
@ -131,8 +140,12 @@ scene = setmetatable({
pop = function()
local previous = scene.current
scene.current = scene.stack[#scene.stack-1]
scene.timer = scene.current.timer
if previous then previous:exit() end
scene.timer = scene.current and scene.current.timer or nil
scene.signal = scene.current and scene.current.signal or nil
if previous then
previous:exit()
if timer then previous.timer:clear() end
end
if scene.current then scene.current:resume() end
table.remove(scene.stack)
end,
@ -164,12 +177,7 @@ scene = setmetatable({
draw = function(...)
if scene.current then scene.current:draw(...) end
end
}, {
--- scene(...) is a shortcut for scene.new(...)
__call = function(self, ...)
return scene.new(...)
end
})
}
-- Bind signals
if signal then