From 336f4f01a56cd130a248fdc88bbf2bc4da2a09ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Tue, 20 Sep 2022 13:55:51 +0900 Subject: [PATCH] input: improve value source --- docs/index.html | 2 +- docs/modules/asset.html | 2 +- docs/modules/ecs.html | 2 +- docs/modules/input.html | 62 ++++++++++++++++++---- docs/modules/ldtk.html | 6 +-- docs/modules/scene.html | 2 +- docs/modules/signal.html | 2 +- docs/modules/timer.html | 2 +- docs/modules/ubiquitousse.html | 2 +- docs/modules/util.html | 2 +- docs/topics/LICENSE.html | 2 +- docs/topics/README.md.html | 2 +- input/event.lua | 2 +- input/input.lua | 94 ++++++++++++++++++++++------------ 14 files changed, 129 insertions(+), 55 deletions(-) diff --git a/docs/index.html b/docs/index.html index 2055bf1..2d52519 100644 --- a/docs/index.html +++ b/docs/index.html @@ -113,7 +113,7 @@
generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
diff --git a/docs/modules/asset.html b/docs/modules/asset.html index 8ab79b3..aa212a6 100644 --- a/docs/modules/asset.html +++ b/docs/modules/asset.html @@ -337,7 +337,7 @@
generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
diff --git a/docs/modules/ecs.html b/docs/modules/ecs.html index ba4d9f5..3b01f9c 100644 --- a/docs/modules/ecs.html +++ b/docs/modules/ecs.html @@ -1729,7 +1729,7 @@ its sibling systems (i.e. completely stop the propagation of the event).
generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
diff --git a/docs/modules/input.html b/docs/modules/input.html index a6afc72..6c6270a 100644 --- a/docs/modules/input.html +++ b/docs/modules/input.html @@ -278,11 +278,11 @@ end mouse.x - Mouse input: X position of the mouse cursor. + Mouse input: X position of the mouse cursor in the game window. mouse.y - Mouse input: Y position of the mouse cursor. + Mouse input: Y position of the mouse cursor in the game window. mouse.dx @@ -316,6 +316,10 @@ end child.X Children inputs: current value of a child input of the current input. + + value + Current input: current value of the current input. +
@@ -418,7 +422,14 @@ player.fire.event:bind("pressed", "key.right - key.left, key.down - key.up", dimension = 2 } - special = { + mouse = { + -- Example input that returns the position of the mouse cursor, that can be controlled both using an actual mouse or + -- through a joystick. + dimension = 2, + "mouse.x, mouse.y", + { "value[1] + axis.leftx * speed * dt, value[2] + axis.lefty * speed * dt", speed = 300 } -- contains dt, so updated only once per frame. Thus speed here is the speed in pixel/second, as expected. + } + mouse = { -- A special case: if the dt source is present in an expression, it will make every other input source passive by default in the expression. -- This is the case since dt will trigger an update every frame, and is therefore mostly relevant for input that is used once per frame only -- (while other input sources might cause the input to update several times per frame). @@ -798,7 +809,7 @@ player.fire.event:bind("pressed", reload to apply changes.

+

Can be changed anytime, but you will need to call reload to apply changes.

See expressions for an explanation on how to write input expressions. @@ -1294,7 +1305,8 @@ player.fire.event:bind("pressed", Input sources

- Input sources are the initial source of input data. They are identified by a Lua identifier name. + Input sources are the initial source of input data; each input source can return a single number value. + They are identified by a Lua identifier name. See expressions on how to use them in expressions.

Input sources are provided for common input methods (keyboard, mouse, gamepad) by default; see below for a list of built-in input sources.

@@ -1385,7 +1397,7 @@ player.fire.event:bind("pressed", "pressed", "pressed", "pressed", + value + +
+ Current input: current value of the current input. + For inputs of dimension 1.

+ +

If the input is of dimension at least 2, instead use value[N], which gives the + current value of the Nth dimension of the current input. + Replace N with the index of the dimension you want.

+ +

Note that is input is passive by default. + Think twice before marking it active as this may create a feedback loop (the input being updated will trigger it to be updated again, and so on). + + + + + + +

    +
  • value + + +
  • +
+ + + + +
@@ -1592,7 +1636,7 @@ player.fire.event:bind("pressed",
generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
diff --git a/docs/modules/ldtk.html b/docs/modules/ldtk.html index 864704d..09e1577 100644 --- a/docs/modules/ldtk.html +++ b/docs/modules/ldtk.html @@ -2066,7 +2066,7 @@ end Level background.

If there is a background image, background.image contains a table {image=image, x=number, y=number, sx=number, sy=number} - where image is the LÖVE image (or image filepath if LÖVE not available) x and y are the top-left position, + where image is the LÖVE image (or image filepath if LÖVE not available) x and y are the top-left position, and sx and sy the horizontal and vertical scale factors. @@ -2142,7 +2142,7 @@ end

  • Enum are converted into a Lua string giving the currently selected enum value.
  • Filepath are converted into a Lua string giving the file path.
  • Arrays are converted into a Lua table with the elements in it as a list.
  • -
  • Points are converted into a Lua table with the fields x and y, in pixels: { x=number, y=number }.
  • +
  • Points are converted into a Lua table with the fields x and y, in pixels: { x=number, y=number }.
  • Colors are converted into a Lua table with the red, green and blue components in [0-1] as a list: {r,g,b}.
  • Tiles are converted into a Lua table { tileset = associated tileset object, quad = associated quad } where quad is a LÖVE Quad if LÖVE is available, otherwise a table { x, y, width, height }.
  • EntityRef are converted into a Lua table { level = level, layerIid = layer IID, entityIid = entity IID, entity = see explanation }. If the entity being refernced belongs to another level and this level is not loaded, entity will be nil; otherwise (same level or the other level is also loaded), it will contain the entity.
  • @@ -2170,7 +2170,7 @@ end
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/modules/scene.html b/docs/modules/scene.html index 692ab51..aadeb83 100644 --- a/docs/modules/scene.html +++ b/docs/modules/scene.html @@ -703,7 +703,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/modules/signal.html b/docs/modules/signal.html index 359b7a6..3eafbd1 100644 --- a/docs/modules/signal.html +++ b/docs/modules/signal.html @@ -788,7 +788,7 @@ signal.event:bind("keypressed", function(key, scancode) print("pr
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/modules/timer.html b/docs/modules/timer.html index 7efb9d4..fd8f9ca 100644 --- a/docs/modules/timer.html +++ b/docs/modules/timer.html @@ -1154,7 +1154,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/modules/ubiquitousse.html b/docs/modules/ubiquitousse.html index 8413201..0a87b43 100644 --- a/docs/modules/ubiquitousse.html +++ b/docs/modules/ubiquitousse.html @@ -394,7 +394,7 @@ the repository to save you a few seconds.

    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/modules/util.html b/docs/modules/util.html index 09748e1..d83fe32 100644 --- a/docs/modules/util.html +++ b/docs/modules/util.html @@ -785,7 +785,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/topics/LICENSE.html b/docs/topics/LICENSE.html index 6f2ede6..c85b7e9 100644 --- a/docs/topics/LICENSE.html +++ b/docs/topics/LICENSE.html @@ -65,7 +65,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/docs/topics/README.md.html b/docs/topics/README.md.html index f868198..bf62a8c 100644 --- a/docs/topics/README.md.html +++ b/docs/topics/README.md.html @@ -87,7 +87,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-09-20 01:08:14 +Last updated 2022-09-20 13:52:55
    diff --git a/input/event.lua b/input/event.lua index 00c4b94..012bdc6 100644 --- a/input/event.lua +++ b/input/event.lua @@ -33,7 +33,7 @@ local function joystickAxisFilter(input, new, joystick) if input._joystick and joystick:getID() ~= input._joystick:getID() then return nil -- ignore if not from the selected joystick end - local deadzone = input:_deadzone() + local deadzone = input._deadzone if math.abs(new) < deadzone then return 0 -- apply deadzone on axis value else diff --git a/input/input.lua b/input/input.lua index cf4c955..42a159f 100644 --- a/input/input.lua +++ b/input/input.lua @@ -60,12 +60,15 @@ local signal = require((...):gsub("input%.input$", "signal")) local event = require((...):gsub("input$", "event")) local abs, sqrt, floor, ceil, min, max = math.abs, math.sqrt, math.floor, math.ceil, math.min, math.max +local unpack = table.unpack or unpack -- TODO: friendly name for sources -- TODO: way to handle text input -- don't want to change how everything is number based here (it's clean), but would be ok to eg give an additionnal metdatat (text string) along with the "text key pressed" input +-- TODO: might be interesting to allow for some outputs, like rumble for a player's joystick? + -- Table to contain temporary calculations (mainly to compute input deltas without needing to allocate a new table). -- Note that this table is never cleared; don't fill it with large data and don't assume its length. local tmp = {} @@ -164,7 +167,14 @@ end -- "key.right - key.left, key.down - key.up", -- dimension = 2 -- } --- special = { +-- mouse = { +-- -- Example input that returns the position of the mouse cursor, that can be controlled both using an actual mouse or +-- -- through a joystick. +-- dimension = 2, +-- "mouse.x, mouse.y", +-- { "value[1] + axis.leftx * speed * dt, value[2] + axis.lefty * speed * dt", speed = 300 } -- contains dt, so updated only once per frame. Thus speed here is the speed in pixel/second, as expected. +-- } +-- mouse = { -- -- A special case: if the `dt` source is present in an expression, it will make every other input source passive by default in the expression. -- -- This is the case since `dt` will trigger an update every frame, and is therefore mostly relevant for input that is used once per frame only -- -- (while other input sources might cause the input to update several times per frame). @@ -283,7 +293,7 @@ input_mt = { -- This table does not contain any userdata and should be easily serializable (e.g. to save custom input binding config). -- This doesn't include input state, grab state, the event registry and the selected joystick since they may change often during runtime. -- - -- Can be changed anytime, but you may need to call `reload` to apply changes. + -- Can be changed anytime, but you will need to call `reload` to apply changes. -- -- See [expressions](#Input_expressions) for an explanation on how to write input expressions. -- @usage @@ -353,6 +363,8 @@ input_mt = { _boundSourceEvents = {}, -- Map of sources events that are binded (and thus will send events to _afterFilterEvent). _joystick = nil, -- Currently selected joystick for this player. Also shared with children inputs. _dimension = 1, -- Dimension of the input. + _deadzone = 0.05, -- Deadzone of the input. + _threshold = 0.05, -- Threshold of the input. --- Update the input and its children. -- Should be called every frame, typically _after_ you've done all your input handling @@ -377,8 +389,11 @@ input_mt = { --- Reload the input `config`, and do the same for its children. -- This will reenable the input if it was disabled using `disable`. reload = function(self) - -- resize dimensions + -- get main options self._dimension = self.config.dimension or 1 + self._deadzone = self.config.deadzone or 0.05 + self._threshold = self.config.threshold or 0.05 + -- resize dimensions if #self._value > self._dimension then for i=self._dimension+1, #self._value do self._value[i] = nil @@ -434,7 +449,6 @@ input_mt = { for k, v in pairs(args) do env[k] = v end setmetatable(env, { __index = function(t, key) - if key == "value" then return self:value() end return self._sourceCache[key] or expressionEnv[key] end }) @@ -472,7 +486,7 @@ input_mt = { return setmetatable({ _ = i or #sources }, srcmt) end } - local scanEnv = setmetatable({ value = 0 }, { __index = srcmt.__index }) -- value is not a source + local scanEnv = setmetatable({}, { __index = srcmt.__index }) -- value is not a source for k, v in pairs(args) do scanEnv[k] = v end -- add args for k in pairs(expressionEnv) do scanEnv[k] = zero end -- add functions for _, mod in ipairs(sourceModifiers) do -- add modifiers functions @@ -521,21 +535,33 @@ input_mt = { local cname, index = sname:match("^child%.(.*)%[(%d+)%]$") if not cname then cname = sname:match("^child%.(.*)$") end local child = self.children[cname] - assert(child, ("input expression refer to %s but this input has no child named %s"):format(sname, cname)) + assert(child, ("input expression refer to %q but this input has no child named %s"):format(sname, cname)) if child._dimension > 1 then - assert(index, ("input expression refer to %s but this child only has more than one dimension"):format(sname)) - self._event:bind(self.children[cname].event, "moved", function(...) -- child event -> self._afterFilterEvent link - local new = select(tonumber(index), ...) - s.parentCache[s.lastKey] = new - self._afterFilterEvent:emit(sname, new) - end) + assert(index, ("input expression refer to %q without specifing a dimension but this child has more than one dimension"):format(sname)) else - assert(not index, ("input expression refer to %s but this child only has a single dimension"):format(sname)) - self._event:bind(self.children[cname].event, "moved", function(new) -- child event -> self._afterFilterEvent link - s.parentCache[s.lastKey] = new - self._afterFilterEvent:emit(sname, new) - end) + assert(not index, ("input expression refer to %q but this child only has a single dimension"):format(sname)) end + local i = index and tonumber(index) or 1 + self._event:bind(self.children[cname].event, "moved", function(...) -- child event -> self._afterFilterEvent link + local new = select(i, ...) + s.parentCache[s.lastKey] = new + self._afterFilterEvent:emit(sname, new) + end) + elseif sname:match("^value") then + local index = sname:match("^value%[(%d+)%]$") + if not index then assert(sname == "value", ("%q is not a valid source; value should be either \"value\" or \"value[number]\""):format(sname)) end + s.passive = true + if self._dimension > 1 then + assert(index, ("input expression refer to %q without specifing a dimension but this input has more than one dimension"):format(sname)) + else + assert(not index, ("input expression refer to %q but this input only has a single dimension"):format(sname)) + end + local i = index and tonumber(index) or 1 + self._event:bind(self.event, "moved", function(...) -- self event -> self._afterFilterEvent link + local new = select(i, ...) + s.parentCache[s.lastKey] = new + self._afterFilterEvent:emit(sname, new) + end) else self._event:bind(event, sname, function(new, filter, ...) -- event source -> self._afterFilterEvent link if filter then @@ -584,7 +610,7 @@ input_mt = { new = filterfn(self, new, ...) if new == nil then return end end - if abs(new) >= self:_threshold() then + if abs(new) >= self._threshold then event:unbind("_active", onevent) fn(source) end @@ -701,7 +727,7 @@ input_mt = { if self.grabbed then self.grabbed:_update(new) -- pass onto grabber else - local threshold = self:_threshold() + local threshold = self._threshold -- update values new = new or self._value local old = self._value @@ -736,20 +762,13 @@ input_mt = { end end end - end, - -- Returns the deadzone of the input. - _deadzone = function(self) - return self.config.deadzone or 0.05 - end, - -- Returns the threshold of the input. - _threshold = function(self) - return self.config.threshold or 0.05 - end, + end } input_mt.__index = input_mt --- Input sources. --- Input sources are the initial source of input data. They are identified by a Lua identifier name. +-- Input sources are the initial source of input data; each input source can return a single number value. +-- They are identified by a Lua identifier name. -- See [expressions](#Input_expressions) on how to use them in expressions. -- -- Input sources are provided for common input methods (keyboard, mouse, gamepad) by default; see below for a list of built-in input sources. @@ -776,10 +795,10 @@ input_mt.__index = input_mt -- N is either 1 for the primary mouse button, 2 for secondary or 3 for middle button. -- @field mouse.N ---- Mouse input: X position of the mouse cursor. +--- Mouse input: X position of the mouse cursor in the game window. -- @field mouse.x ---- Mouse input: Y position of the mouse cursor. +--- Mouse input: Y position of the mouse cursor in the game window. -- @field mouse.y --- Mouse input: latest X movement of the mouse cursor. @@ -817,7 +836,7 @@ input_mt.__index = input_mt -- @field dt --- Children inputs: current value of a child input of the current input. --- For child input of dimension 1. +-- For child inputs of dimension 1. -- Replace X with the name of the child input. -- -- If the child input is of dimension at least 2, instead use `child.X[N]`, which gives the @@ -825,4 +844,15 @@ input_mt.__index = input_mt -- Replace X with the name of the child input, and N with the index of the dimension you want. -- @field child.X +--- Current input: current value of the current input. +-- For inputs of dimension 1. +-- +-- If the input is of dimension at least 2, instead use `value[N]`, which gives the +-- current value of the Nth dimension of the current input. +-- Replace N with the index of the dimension you want. +-- +-- Note that is input is passive by default. +-- Think twice before marking it active as this may create a feedback loop (the input being updated will trigger it to be updated again, and so on). +-- @field value + return make_input