godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.75k stars 21.12k forks source link

Input get_vector returns wrong values after switching devices #82148

Closed jojorne closed 7 months ago

jojorne commented 1 year ago

Bugsquad note: This issue has been confirmed several times already. No need to confirm it further.


Godot version

4.2.dev5

System information

Windows 11

Issue description

When I press the "UI Right" key on my keyboard, I get a value for Y like 0.01937 instead of 0.0 but there is a step to reproduce this. You'll need two devices, a keyboard and a joypad.

Steps to reproduce

Minimal reproduction project

N/A

jojorne commented 1 year ago

Oh! I want to mention that Y returns to 0.0 after you press "up" and "down" keys.

Calinou commented 1 year ago

Can you test https://github.com/godotengine/godot/pull/82056 locally? See Testing pull requests in the documentation for instructions.

NibblyPig commented 1 year ago

This is controller drift, I almost opened a bug for this but I am not sure if it's intended because I had the same issue.

Basically there's the anti-drift thing in the Input settings, but it applies to the whole input not just the gamepad.

So if you're doing nothing, your Input.get_vector will be 0,0

If you press keyboard left, then it disables the drift threshold and you will get -1,0 combined with whatever your drift is.

This means that if you press left, and your controller drift is up/down, you can start moving up even though you're pressing left.

IMO it's a bug because you should apply the drift threshold only to the gamepad. However it is kinda by design. Workaround would be to get_vector twice, once on the keyboard and once on the gamepad if it's important you have both control the same character, and combine the vectors (the gamepad vector will be 0,0 if it's not moving this way).

jojorne commented 1 year ago

Yes, but as I mentioned, if you press up/down, the added drift disappear. Or this drift should be applied all the time or not at all. Also it doesn't happen with the joypad. Perhaps because keyboard only send 0 or 1. Or perhaps because the drift cap doesn't apply to keyboard? idk... So there's definitely something there because the way it works is not constant.

jojorne commented 1 year ago

Can you test #82056 locally? See Testing pull requests in the documentation for instructions.

I just tested the https://github.com/godotengine/godot/pull/82056 and it doesn't fix it. image

It start like this: image

And after pressing the keyboard left/right/up/down it becomes like this: image

After pressing a key, the corresponding value of that key becomes 0. image After pressing the right key it makes the right value 0. Next, if I press up/down key it will make that value 0.

jojorne commented 1 year ago

Input.get_action_strength() kind of works! I don't need to press anything and I can switch between devices. image

But there's a problem - when a value gets closer to 1, it becomes 1 and because of that it's hard to move diagonally. This behavior is not the same as get_action_raw_strength().

Edit: Here's a side-by-side comparison. They are rounded, Y value becomes 0 and X becomes 1. image get_action_strength() makes it good for a grid like movement.

Changing the deadzone smooth the roundness on get_action_strength() but... image

The values are still not the same. Maybe another bug here? 🐛 idk... image

And a deadzone of 0 effective turns get_action_strength() into get_action_raw_strength(). image

Which causes the problem to return... image

ErezShahaf commented 1 year ago

If you mean that you leave the joystick completely and the Y value returns to a small value when the keyboard uses the X axis, then it would indicate that some values used by the joystick are not reset in the first frame that the joystick is "unpressed" and you keep a small value.

Anyway, I think that there is a chance that your issue has to do something with deadzone missunderstanding, and isn't an actual bug. https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-get-action-raw-strength

If it turns out to be a bug, please share with me minimal project and I would love to be assigned to solve it.

jojorne commented 1 year ago

I think that there is a chance that your issue has to do something with deadzone missunderstanding, and isn't an actual bug. https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-get-action-raw-strength

I don't think it's the joypad. As the drift of 0.01 on the screenshots above is too small and no other game has any kind of drifting happening yet. Also a quote from the docs:

The downside is that without a deadzone system, an analog axis' strength will never be equal to 0.0 due to how the controller is physically built.

Font: https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html

If you mean that you leave the joystick completely and the Y value returns to a small value when the keyboard uses the X axis, then it would indicate that some values used by the joystick are not reset in the first frame that the joystick is "unpressed" and you keep a small value.

But yes, I also think so. After switching from joypad to keyboard, the joypad's values are not reset until you press the corresponding keys on the keyboard. So I think there's a bug there.

If it turns out to be a bug, please share with me minimal project and I would love to be assigned to solve it.

A minimum project is: Attach a script to a brand new Node2D scene then get the values from Input.

get_vector() internally uses get_action_raw_strength(). These methods, get_action_raw_strength() and get_action_strength(), gets the value from action_state. So somewhere in action_state the values are cached.

adamscoble commented 1 year ago

I'm seeing this issue as well with get_vector. With a controller connected my Y value isn't 0 when using a left/right action on the keyboard. I've tried high deadzone values and it doesn't seem to fix it, so I don't believe it's a deadzone issue.

warent commented 11 months ago

If you mean that you leave the joystick completely and the Y value returns to a small value when the keyboard uses the X axis, then it would indicate that some values used by the joystick are not reset in the first frame that the joystick is "unpressed" and you keep a small value.

Anyway, I think that there is a chance that your issue has to do something with deadzone missunderstanding, and isn't an actual bug. https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-get-action-raw-strength

If it turns out to be a bug, please share with me minimal project and I would love to be assigned to solve it.

@ErezShahaf I've got a reproduction of this issue (or one very similar) here: https://github.com/godotengine/godot/issues/85076

jojorne commented 11 months ago

The solution I found was splitting the inputs (e.g., "ui_left_joy"). One for Joypad and one for keyboard and mouse.

Calinou commented 11 months ago

The solution I found was splitting the inputs (e.g., "ui_left_joy"). One for Joypad and one for keyboard and mouse.

This was fixed in 4.2.beta6 and later by https://github.com/godotengine/godot/pull/84685. Please test 4.2.rc1 and report back :slightly_smiling_face:

jojorne commented 11 months ago

I don't think it's fixed yet =\

image image image

arthurpalm commented 9 months ago

Still happening in 4.2.1 and 4.3dev1

silverhammermba commented 8 months ago

Definitely still happening in 4.2.1. Easy to check with this code

var vec := Input.get_vector("ui_left", "ui_right", "ui_down", "ui_up")
if vec != Vector2.ZERO:
    push_warning("%f %f" % [vec.x, vec.y])

Press directions on keyboard: looks fine. Move left joystick on controller: looks fine. Switch back to keyboard and press directions: wrong. Pressing left on keyboard now consistently outputs -0.75, -0.03

romlok commented 8 months ago

Currently, get_vector gets the raw strength of each direction, combines them into a single vector, and then applies a deadzone to that vector. Thus the raw drift noise from analogue sticks nudges the direction of the combined vector slightly away from pure cardinal directions.

So one solution to this issue could be for get_vector to apply a deadzone to each direction's value, before being combined into the single vector. That way the noisy small inputs from analogue sticks would be filtered out. However, that would then make the deadzone rectangular, which I believe is usually less desirable than a circular deadzone. :thinking:

Another option would be to introduce some sort of filter for analogue inputs, such that any value below a certain threshold is ignored even by a "raw" strength check. Or leave "raw" as it is, and add a get_action_filtered_strength function as well! :sweat_smile:

Or just add a warning to the documentation, that get_vector shouldn't be used when an action has both digital and analogue inputs assigned to it? :disappointed:

romlok commented 8 months ago

Oop, unless I'm misunderstanding, this seems like the same issue as https://github.com/godotengine/godot/issues/55264?

tkgalk commented 7 months ago

Working on a twinstick shooter now and according to the documentation get_vector() is the one to use. There should be a note about this in the documentation and the proper way to handle them (https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html#controllers-gamepads-and-joysticks), but currently documentation very confidently states:

But if the controller is still connected it causes the character to drift a side as the residual input is picked up from the controller the moment the keyboard is used. Definitely NOT an expected behaviour and contrary to documentation, forces you to handle controller and MKB separated. Either the function should be changed or the documentation updated.

Calinou commented 7 months ago

Thanks for the report! Consolidating in https://github.com/godotengine/godot/issues/55264.

jojorne commented 7 months ago

Was it fixed? 🤔

Oh! Okay... nvm 😅