How to prevent drifting from analog movement?

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
DOMINOSRULZ
Prole
Posts: 15
Joined: Mon Jul 27, 2020 3:18 am
Contact:

How to prevent drifting from analog movement?

Post by DOMINOSRULZ »

Hello,
I have been trying to get input movement from a controller working in a project I am working on, but I am stuck on the movement regarding the analog stick. I have added something like this for the movement:

Code: Select all

local xaxis = joystick:getAxis(1)
downPaddle.x = downPaddle.x + (xaxis * downPaddle.moveSpeed)
It does move the player correctly, but when the stick is not moved in any direction, the player starts drifting instead of stopping at once. From what I understand, there needs to be a way for the game to know that the analog stick has stopped being used. However, I cannot find any way to do this. I've tried thinking of some solutions myself, but to no avail.

Are there any examples as to how this can be fixed?
MrFariator
Party member
Posts: 525
Joined: Wed Oct 05, 2016 11:53 am

Re: How to prevent drifting from analog movement?

Post by MrFariator »

All analogue sticks tend to report some infinitely small, miniscule movements near the default resting point, particularly if the gamepad has seen some regular use over the years. Like, yes, physically it might appear like the stick should be resting at 0, but either the stick has worn down (and as such can't rest in the center perfectly anymore), or the sensors are more sensitive than you are expecting.

So, usually the go-to answer is to set a certain "deadzone" for the analog stick readings. Meaning, the value needs to surpass a certain value, before the game starts registering it, and actually start acting on it. For example, on an Xbox Series X gamepad, I think the value reported by getAxis() should perhaps be around 0.1 or higher, before it's detected as movement. You may adjust the minimum value up or down according to your liking, or which brand of gamepads you're working with. Some games provide the option to set the deadzone size, but this is not strictly necessary so long you find a range that works for the most common brands.

In your case specifically, because the paddle movement speed seems to be tied to how much you tilt the joystick, you might want to also convert the read value from [minimum,1] to [0,1] (basically normalize it), if you want to make it a smoother gradient.
User avatar
pgimeno
Party member
Posts: 3593
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to prevent drifting from analog movement?

Post by pgimeno »

Here's a function to do what MrFariator said. Generally, I'm all for teaching how to fish instead of giving fish, but I made an exception in this case because it's not easy for everyone to work out the linear transform that makes the dead zone work.

In addition, the most common formulation may suffer from a precision problem where it might not return exactly 1 or -1 when the joystick function does, and I've written this function with that in mind. It will return exactly 0 when within the dead zone, and exactly 1 or -1 when joystick:getAxis does. It also returns -0 when within the negative part of the dead zone, allowing you to check the zero crossing during testing.

Code: Select all

local DeadZone = 0.1

local function getAxis(joystick, axis)
  local value = joystick:getAxis(axis)
  local sign = value < 0 and -1 or 1
  local DynamicRange = 1 - DeadZone
  value = math.min(1 - math.abs(value), DynamicRange)
  return sign * (1 - value / DynamicRange)
end
Just replace joystick:getAxis(n) with getAxis(joystick, n) to make the deadzone work, and adjust the deadzone to your liking.
DOMINOSRULZ
Prole
Posts: 15
Joined: Mon Jul 27, 2020 3:18 am
Contact:

Re: How to prevent drifting from analog movement?

Post by DOMINOSRULZ »

MrFariator wrote: Sun May 26, 2024 9:50 am All analogue sticks tend to report some infinitely small, miniscule movements near the default resting point, particularly if the gamepad has seen some regular use over the years. Like, yes, physically it might appear like the stick should be resting at 0, but either the stick has worn down (and as such can't rest in the center perfectly anymore), or the sensors are more sensitive than you are expecting.

So, usually the go-to answer is to set a certain "deadzone" for the analog stick readings. Meaning, the value needs to surpass a certain value, before the game starts registering it, and actually start acting on it. For example, on an Xbox Series X gamepad, I think the value reported by getAxis() should perhaps be around 0.1 or higher, before it's detected as movement. You may adjust the minimum value up or down according to your liking, or which brand of gamepads you're working with. Some games provide the option to set the deadzone size, but this is not strictly necessary so long you find a range that works for the most common brands.

In your case specifically, because the paddle movement speed seems to be tied to how much you tilt the joystick, you might want to also convert the read value from [minimum,1] to [0,1] (basically normalize it), if you want to make it a smoother gradient.
pgimeno wrote: Sun May 26, 2024 12:38 pm Here's a function to do what MrFariator said. Generally, I'm all for teaching how to fish instead of giving fish, but I made an exception in this case because it's not easy for everyone to work out the linear transform that makes the dead zone work.

In addition, the most common formulation may suffer from a precision problem where it might not return exactly 1 or -1 when the joystick function does, and I've written this function with that in mind. It will return exactly 0 when within the dead zone, and exactly 1 or -1 when joystick:getAxis does. It also returns -0 when within the negative part of the dead zone, allowing you to check the zero crossing during testing.

Code: Select all

local DeadZone = 0.1

local function getAxis(joystick, axis)
  local value = joystick:getAxis(axis)
  local sign = value < 0 and -1 or 1
  local DynamicRange = 1 - DeadZone
  value = math.min(1 - math.abs(value), DynamicRange)
  return sign * (1 - value / DynamicRange)
end
Just replace joystick:getAxis(n) with getAxis(joystick, n) to make the deadzone work, and adjust the deadzone to your liking.
Thank you both very much. The solution you both proposed worked, and the paddle moves just as it is supposed to. I have also taken notes on what was said, and will very much keep this in mind when working on my next project if it ever needs controller support.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 4 guests