Yes, when you make a circle on your controller stick, it would make sense to expect the input to make at least roughly a circle too - which it doesn't. Even if a brand new controller was able to make a perfect square with it's circular movement the raw input data would still have to be processed a little bit to make it useable in a game (assuming that accuracy of the stick input is important in the specific game).
Code: Select all
function love.load()
deadzone = 0.25 -- adjustable, my pretty worn controller needs to have this as high as 0.3
scale, ix, iy = 50, 50, 450 -- adjustable (scale == radius of input circle)
love.graphics.setBackgroundColor(30, 30, 30)
joystick = love.joystick.getJoysticks()[1]
s = {}
trace = {}
trace.show = true
cursor = { x = 250, y = 250, speed = 200, useRawInput= false }
end
function love.update(delta)
s.ax, s.ay = joystick:getAxes() -- ax and ay are the actual raw values from the controller
local extent = math.sqrt(math.abs(s.ax * s.ax) + math.abs(s.ay * s.ay))
local angle = math.atan2(s.ay, s.ax)
if (extent < deadzone) then
s.x, s.y = 0, 0 -- x and y are the rectified inputs
else
extent = math.min(1, (extent - deadzone) / (1 - deadzone))
s.x, s.y = extent * math.cos(angle), extent * math.sin(angle)
end
table.insert(trace, 1, iy + (scale*s.ay))
table.insert(trace, 1, ix + (scale*s.ax))
-- move cursor
local dx, dy = 0, 0
if cursor.useRawInput then dx, dy = s.ax, s.ay
else dx, dy = s.x, s.y end
cursor.x = cursor.x + (delta * cursor.speed * dx)
cursor.y = cursor.y + (delta * cursor.speed * dy)
-- prevent cursor from leaving screen
if cursor.x <= 0 then cursor.x = 0 end
if cursor.y <= 0 then cursor.y = 0 end
if cursor.x >= 500 then cursor.x = 500 end
if cursor.y >= 500 then cursor.y = 500 end
end
function drawInputIndicator(drawTrace)
-- bullseye
love.graphics.setColor(20, 10, 20)
love.graphics.circle("fill", ix, iy, scale, 48)
love.graphics.setColor(30, 30, 30)
love.graphics.circle("fill", ix, iy, scale * deadzone, 24)
-- actual input values indicator line
if drawTrace then love.graphics.setColor(160, 80, 160) -- visual feedback for if trace is being shown or nay
else love.graphics.setColor(100, 160, 100) end
love.graphics.circle("fill", ix, iy, scale / 40, 8)
love.graphics.setColor(200, 100, 200)
love.graphics.line(ix, iy, ix + (s.ax * scale), iy + (s.ay * scale))
-- interpreted input indicator circle
love.graphics.setColor(150, 120, 150)
love.graphics.circle("line", ix + (s.x * scale), iy + (s.y * scale), scale / 20, 12)
if drawTrace and #trace >= 4 then love.graphics.line(trace) end
end
function love.draw()
drawInputIndicator(trace.show)
--cursor
if cursor.useRawInput then love.graphics.setColor(240, 120, 120)
else love.graphics.setColor(200, 240, 120) end -- green cursor when using interpreted input
love.graphics.line(cursor.x - 5, cursor.y, cursor.x + 5, cursor.y)
love.graphics.line(cursor.x, cursor.y - 5, cursor.x, cursor.y + 5)
end
function love.joystickpressed(joystick, button)
if button == 1 then
trace = {}
trace.show = true
elseif button == 2 then
trace.show = not trace.show
elseif button == 3 then
cursor.useRawInput = not cursor.useRawInput
end
end
Button 1 (a) - Reset indicator trace
Button 2 (b) - Show/hide trace
Button 3 (x) - Switch between raw and processed input
Red cursor means that it's using the raw controller input to transform it's position, green uses the processed values. You can notice the cursor moving in a much less consistent manner when using the raw mode. If this was some sort of a diagonal running race game, the winner would be the one with the newer controller
You can use the processed input values to move a character (or a cursor, in this example) and expect it to move in a consistent way with these two lines:
cursor.x = cursor.x + (delta * cursor.speed * s.x)
cursor.y = cursor.y + (delta * cursor.speed * s.y)