1
0
Fork 0
mirror of https://github.com/Reuh/ubiquitousse.git synced 2025-10-27 09:09:30 +00:00
ubiquitousse/input/input.lua
Étienne Reuh Fildadut 4b75f21e52 Remove backend system and ctruLua support
Since I only use the LÖVE backend anyway, this simplifies the code.
Tidied some code.
2021-07-18 19:30:43 +02:00

205 lines
8.4 KiB
Lua

--- ubiquitousse.input
-- Depends on a backend.
-- Optional dependencies: ubiquitousse.signal (to bind to update signal in signal.event)
local loaded, signal = pcall(require, (...):match("^(.-)input").."signal")
if not loaded then signal = nil end
-- TODO: some key selection helper? Will be backend-implemented, to account for all the possible input methods.
-- TODO: some way to list all possible input / outputs, or make the *inUse make some separation between inputs indiscutitably in use and those who are incertain.
-- TODO: outputs! (rumble, lights, I don't know)
-- TODO: other, optional, default/generic inputs, and a way to know if they are binded.
-- TODO: multiplayer input helpers? something like getting the same input for different players, or default inputs for different players
-- FIXME https://love2d.org/forums/viewtopic.php?p=241434#p241434
local button_mt
local axis_mt
local pointer_mt
--- Input stuff
-- Inspired by Tactile by Andrew Minnich (https://github.com/tesselode/tactile), under the MIT license.
-- Ubiquitousse considers two basic input methods, called buttons (binary input) and axes (analog input).
local input
input = {
--- Used to store inputs which were updated this frame
-- { Input: true, ... }
-- This table is for internal use and shouldn't be used from an external script.
updated = {},
dt = 0,
---------------------------------
--- Detectors (input sources) ---
---------------------------------
-- Buttons detectors --
-- A button detector is a function which returns true (pressed) or false (unpressed).
-- All buttons are identified using an identifier string, which depends on the backend. The presence of eg., a mouse or keyboard is not assumed.
-- Some identifier strings conventions: (not used internally by Ubiquitousse, but it's nice to have some consistency between backends)
-- They should be in the format "source1.source2.[...].button", for example "keyboard.up" or "gamepad.button.1.a" for the A-button of the first gamepad.
-- If the button is actually an axis (ie, the button is pressed if the axis value passes a certain threshold), the threshold should be in the end of the
-- 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.
-- @tparam string button identifier, depends on the platform Ubiquitousse is running on
-- @treturn the new button detector
-- @impl love
basicButtonDetector = function(str) end,
--- Make a new button detector from a detector function, string, or list of buttons.
-- @tparam string, function button identifier
buttonDetector = function(obj)
if type(obj) == "function" then
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,
-- Axis detectors --
-- Similar to buttons detectors, but returns a number between -1 and 1.
-- Threshold value can be used similarly with %.
-- Axis detectors can also be defined by two buttons: if the 1rst button is pressed, value will be -1, if the 2nd is pressed it will be 1
-- and if none or the both are pressed, the value will be 0. This kind of axis identifier is a table {"button1", "button2"}.
-- Axis detectors may also optionally return after the number between -1 and 1 the raw value and max value. The raw value is between -max and +max.
--- Makes a new axis detector from a identifier string.
-- The function may error if the identifier is incorrect.
-- @tparam string axis identifier, depends on the platform Ubiquitousse is running on
-- @treturn the new axis detector
-- @impl love
basicAxisDetector = function(str) end,
--- Make a new axis detector from a detector function, string, or a couple of buttons.
-- @tparam string, function or table axis identifier
axisDetector = function(obj)
if type(obj) == "function" then
return obj
elseif type(obj) == "string" then
return input.basicAxisDetector(obj)
elseif type(obj) == "table" then
local b1, b2 = input.buttonDetector(obj[1]), input.buttonDetector(obj[2])
return function()
local d1, d2 = b1(), b2()
if d1 and d2 then return 0
elseif d1 then return -1
elseif d2 then return 1
else return 0 end
end
end
error(("Not a valid axis detector: %s"):format(obj))
end,
------------------------------
--- Input detection helpers --
------------------------------
-- TODO: make this better
--- Returns a list of the buttons currently in use, identified by their string button identifier.
-- This may also returns "axis threshold" buttons if an axis passes the threshold.
-- @tparam[opt=0.5] number threshold the threshold to detect axes as button
-- @treturn string,... buttons identifiers list
-- @impl love
buttonUsed = function(threshold) end,
--- Returns a list of the axes currently in use, identified by their string axis identifier
-- @tparam[opt=0.5] number threshold the threshold to detect axes
-- @treturn string,... axes identifiers list
-- @impl love
axisUsed = function(threshold) end,
--- Returns a nice name for the button identifier.
-- Can be locale-depedant and stuff, it's only for display.
-- May returns the raw identifier if you're lazy.
-- @tparam string... button identifier string(s)
-- @treturn string... the displayable names
-- @impl love
buttonName = function(...) end,
--- Returns a nice name for the axis identifier.
-- Can be locale-depedant and stuff, it's only for display.
-- May returns the raw identifier if you're lazy.
-- @tparam string... axis identifier string(s)
-- @treturn string... the displayable names
-- @impl love
axisName = function(...) end,
-------------------
--- Other stuff ---
-------------------
--- Some default inputs.
-- The backend should bind detectors to thoses inputs (don't recreate them).
-- These are used to provide some common input default detectors to allow to start a game quickly on
-- any platform without having to configure the keys.
-- If some key function in your game match one of theses defaults, using it instead of creating a new
-- input would be a good idea.
-- @impl love
default = {
pointer = nil, -- Pointer: used to move and select. Example binds: arrow keys, WASD, stick.
confirm = nil, -- Button: used to confirm something. Example binds: Enter, A button.
cancel = nil -- Button: used to cancel something. Example binds: Escape, B button.
},
--- Get draw area dimensions.
-- Used for pointers.
-- @impl love
getDrawWidth = function() return 1 end,
getDrawHeight = function() return 1 end,
--- Update all the Inputs.
-- Should be called at every game update. If ubiquitousse.signal is available, will be bound to the "update" signal in signal.event.
-- The backend can hook into this function to to its input-related updates.
-- @tparam numder dt the delta-time
update = function(newDt)
input.dt = newDt
input.updated = {}
end
--- If you use LÖVE, note that in order to provide every feature (especially key detection), several callbacks functions will
-- need to be called on LÖVE events. See backend/love.lua.
-- If ubiquitousse.signal is available, these callbacks will be bound to signals in signal.event (with the same name as the LÖVE
-- callbacks, minux the "love.").
}
package.loaded[...] = input
button_mt = require((...):gsub("input$", "button"))
axis_mt = require((...):gsub("input$", "axis"))
pointer_mt = require((...):gsub("input$", "pointer"))
-- Constructors
input.button = button_mt._new
input.axis = axis_mt._new
input.pointer = pointer_mt._new
-- Create default inputs
input.default.pointer = input.pointer()
input.default.confirm = input.button()
input.default.cancel = input.button()
-- Bind signals
if signal then
signal.event:bind("update", input.update)
end
require((...):gsub("input$", "love"))
return input