Lua joypad.setanalog sets analog indefinitely
Summary
I would expect joypad.setanalog to only set analog inputs for the current frame as stated here but it seems to set the analog input indefinitely. The only way I have found to give control back to the user is by overriding the analog position using the Virtual Pad tool. I am testing this on the Mupen64plus core with Ocarina of Time.
Repro
If you run the script below on an N64 game you will walk forward forever instead of only for one frame.
joypad.setanalog({
['P1 X Axis'] = 0,
['P1 Y Axis'] = 127,
})
Versions Tested
- BizHawk 2.3
- BizHawk 2.4.2
joypad.setanalog
- void joypad.setanalog(nluatable controls, [object controller = null])
- sets the given analog controls to their provided values for the current frame. Note that unlike set() there is only the logic of overriding with the given value.
Based on the second sentence, you're supposed to override back with something like joypad.setanalog({['P1 X Axis'] = 0, ['P1 Y Axis'] = 0}) when you want the analog sticks to be neutral again.
I have tried that but it still does not return control to the user, any inputs on the controller after the script are ignored.
@RetroEdit
If you continually set it to neutral every frame, then yes, it will override the user's inputs. If you only set it to neutral the first frame you want it to be neutral, and then don't use joypad.set on later inputs, then you ~~won't~~ shouldn't have this problem.
@RetroEdit But even if I just run this script you lose control of the analog stick indefinitely.
joypad.setanalog({
['P1 X Axis'] = 0,
['P1 Y Axis'] = 0,
})
I see. I'll have a look.
EDIT: I'm not too sure either way on this one. Someone else will have to have a look.
One solution could be to set the input as an empty string to regain control, similar to what I did in this script:
https://github.com/TASVideos/BizHawk/blob/master/Assets/Lua/JoypadIntersection.lua
I can try to remember to test it tonight and report back what I find
Alright, something has changed in regards to this in the least few releases. In my old 2.3.2 version of Bizhawk, doing this:
joypad.setanalog({ ['X Axis'] = '', ['Y Axis'] = '', }, 1)
Would allow you to reset the sticks, and give control back to the user. But doing it in 2.4.2 and 2.5 gives an exception. The exception message isn't helpful though, it just says "A .NET exception occured in user-code"
For reference, here is my scipt that works in 2.3.2 but not in later:
local forward = { ['X Axis'] = 0, ['Y Axis'] = 127, }
local resetA = { ['X Axis'] = '', ['Y Axis'] = '', }
local shouldForward = false
local reset = joypad.get(1)
for k,v in pairs(reset) do
reset[k] = null
end
event.unregisterbyname('a')
event.unregisterbyname('e')
event.onframestart( function()
if shouldForward == true then
joypad.setanalog(forward, 1)
end
end, 's' )
event.onframeend( function()
if shouldForward == false then
joypad.setanalog(resetA, 1)
end
end, 'e' )
--------------------------------------
-- Main loop --
--------------------------------------
while true do
if emu.framecount() % 180 == 0 then
shouldForward = not shouldForward
print(shouldForward)
end
emu.frameadvance()
end
Probably introduced by 1042f746f with an incomplete fix in 0f7d29210.
edit: Fixed as below. To reiterate: pass a key-value pair where the value is not a double e.g. {["axisName"] = ""} to not override or undo a previously set override of an axis. I didn't check but from memory a key-value pair in a table where the value is nil is equivalent to not including that pair—use an empty string. Omitting a key from the table passed to joypad.setanalog will continue to use the previously set override if one was set in a previous call.
Maybe the documentation should be fixed to not say the same as the set() function when it does something completely different?
@YoshiRulz the problem isn't the second sentence, but the first: sets the given analog controls to their provided values for the current frame.
... yes, but also forever, unlike set, which only sets buttons for one frame.
pass a key-value pair where the value is not a double e.g. {["axisName"] = ""} to not override or undo a previously set override of an axis. [...] Omitting a key from the table passed to joypad.setanalog will continue to use the previously set override if one was set in a previous call.
This is very important information, none of which is obvious or documented in the source code. I searched for this issue because I too was under the assumption that the current behavior is a bug, but if it's intended, it should be documented as such.
-[LuaMethod("setanalog", "sets the given analog controls to their provided values for the current frame.")]
+[LuaMethod("setanalog", "Sets the given analog controls to their provided values as autoholds. Set axes to the empty string to clear individual holds.")]
?
Sure, that works. But I do wonder whether this API was consciously designed this way or whether it should have worked like joypad.set.
On a related note, OverrideAdapter supports axis overrides just fine, but the documentation says only works with bool buttons... was this not implemented as one-frame override because it didn't work or was it a conscious design decision based on how axis vs. buttons differ in usage?
"Design" is not a word I would use to describe the input subsystem.