BizHawk icon indicating copy to clipboard operation
BizHawk copied to clipboard

Lua joypad.setanalog sets analog indefinitely

Open Sirius902 opened this issue 5 years ago • 10 comments

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

Sirius902 avatar Aug 15 '20 22:08 Sirius902

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.

RetroEdit avatar Aug 15 '20 23:08 RetroEdit

I have tried that but it still does not return control to the user, any inputs on the controller after the script are ignored.

Sirius902 avatar Aug 15 '20 23:08 Sirius902

@RetroEdit

Sirius902 avatar Aug 15 '20 23:08 Sirius902

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 avatar Aug 15 '20 23:08 RetroEdit

@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,
})

Sirius902 avatar Aug 15 '20 23:08 Sirius902

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.

RetroEdit avatar Aug 15 '20 23:08 RetroEdit

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

Gikkman avatar Sep 02 '20 14:09 Gikkman

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

Gikkman avatar Sep 02 '20 19:09 Gikkman

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.

YoshiRulz avatar Sep 02 '20 23:09 YoshiRulz

Maybe the documentation should be fixed to not say the same as the set() function when it does something completely different?

Morilli avatar Sep 18 '24 20:09 Morilli

@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.

Morilli avatar Jun 01 '25 10:06 Morilli

-[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.")]

?

YoshiRulz avatar Jun 01 '25 18:06 YoshiRulz

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?

Morilli avatar Jun 01 '25 19:06 Morilli

"Design" is not a word I would use to describe the input subsystem.

YoshiRulz avatar Jun 01 '25 20:06 YoshiRulz