Page 1 of 1
How to treat analog movement as a button press?
Posted: Mon Sep 24, 2018 2:56 am
by ThiefOfPringles
As the title suggests, I wanna know if there is a way to treat analog movement (on a gamepad) such as the joystick pushing against the edge of its case or the trigger going all the way down as a button press. This idea came from how Nintendo apps (specifically dev with homebrew toolchains) can do this, specifying that the joystick has been pushed up and held like a button press, but I've looked through the entire LOVE wiki only to find that there is no normal way of going about this. Can I have some pointers or examples as to how this could be achieved?
Re: How to treat analog movement as a button press?
Posted: Mon Sep 24, 2018 11:39 am
by JoshGrams
You have to do it yourself, but it's not hard. The values for an axis generally go from -1 to 1, so just check if it's above a threshold. You probably don't want to use 1 as the threshold because they might not be perfectly calibrated, or might drift over time so they hit the case before going all the way to 1.
You may also want to save the last value, and have separate on/off thresholds with a little space between them to avoid jitter (if the axis value is between the thresholds then use the old value).
Here's a quick but (I think) fairly complete demo:
Code: Select all
local function axisValue(self)
local value
if type(self.axis) == 'string' then
value = self.device:getGamepadAxis(self.axis)
elseif type(self.axis) == 'number' then
value = self.device:getAxis(self.axis)
else
error("Unknown axis type.")
end
return value * self.dir
end
local function isAxisButtonDown(self)
local value = axisValue(self)
local down
if value >= self.downThreshold then
down = true
elseif value <= self.upThreshold then
down = false
else
-- indeterminate: use previous value.
down = self.down
end
return down
end
local function updateAxisButton(self)
local down = isAxisButtonDown(self)
if down ~= self.down then
self.down = down
if self.down then self:pressed() else self:released() end
end
end
local function newButtonFromAxis(device, axis, dir)
return {
device = device, axis = axis, dir = dir or 1,
-- Call this in `love.update` to get pressed/released callbacks.
update = updateAxisButton,
pressed = function(button) end,
released = function(button) end,
-- Call this to check whether the button is currently down.
isDown = isAxisButtonDown,
-- Or just read this property after calling `update`.
down = false,
-- Leave a little space between these to avoid possible jitter.
downThreshold = 0.9, upThreshold = 0.75
}
end
function love.load()
buttons = {}
end
function love.draw()
local u = 50
for i,b in ipairs(buttons) do
if b:isDown() then
love.graphics.rectangle('fill', i * u, u, 0.9 * u, 0.9 * u)
end
end
end
function love.joystickadded(j)
if j:isGamepad() then
table.insert(buttons, newButtonFromAxis(j, 'triggerleft'))
table.insert(buttons, newButtonFromAxis(j, 'triggerright'))
else
for i=1,j:getAxisCount() do
table.insert(buttons, newButtonFromAxis(j, i))
end
end
print(#buttons)
end
function love.joystickremoved(j)
for i=#buttons,1,-1 do -- loop backwards so it's safe to remove.
local b = buttons[i]
if b.device == j then table.remove(buttons, i) end
end
end
function love.keypressed(k, s)
if k == 'escape' then love.event.quit() end
end
Re: How to treat analog movement as a button press?
Posted: Tue Sep 25, 2018 2:51 pm
by shru
I'll point out that there are various input libraries which can handle analog inputs this way, in addition to solving other common input issues. I'd take a look at
baton and maybe even my own
otokonokontroller.