How to treat analog movement as a button press?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
Posts: 17
Joined: Fri Feb 26, 2016 1:42 pm

How to treat analog movement as a button press?

Post by ThiefOfPringles » Mon Sep 24, 2018 2:56 am

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?

Posts: 33
Joined: Sat May 27, 2017 10:58 am

Re: How to treat analog movement as a button press?

Post by JoshGrams » Mon Sep 24, 2018 11:39 am

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)
		error("Unknown axis type.")
	return value * self.dir

local function isAxisButtonDown(self)
	local value = axisValue(self)
	local down
	if value >= self.downThreshold then
		down = true
	elseif value <= self.upThreshold then
		down = false
		-- indeterminate: use previous value.
		down = self.down
	return down

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

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

function love.load()
	buttons = {}

function love.draw()
	local u = 50
	for i,b in ipairs(buttons) do
		if b:isDown() then'fill', i * u, u, 0.9 * u, 0.9 * u)

function love.joystickadded(j)
	if j:isGamepad() then
		table.insert(buttons, newButtonFromAxis(j, 'triggerleft'))
		table.insert(buttons, newButtonFromAxis(j, 'triggerright'))
		for i=1,j:getAxisCount() do
			table.insert(buttons, newButtonFromAxis(j, i))

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

function love.keypressed(k, s)
	if k == 'escape' then love.event.quit() end

User avatar
Posts: 26
Joined: Mon Sep 28, 2015 3:56 am
Location: Hyperborea

Re: How to treat analog movement as a button press?

Post by shru » Tue Sep 25, 2018 2:51 pm

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.

Post Reply

Who is online

Users browsing this forum: No registered users and 26 guests