Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axis-based actions can get stuck as pressed #81164

Closed
KoBeWi opened this issue Aug 30, 2023 · 10 comments · Fixed by #81170
Closed

Axis-based actions can get stuck as pressed #81164

KoBeWi opened this issue Aug 30, 2023 · 10 comments · Fixed by #81170

Comments

@KoBeWi
Copy link
Member

KoBeWi commented Aug 30, 2023

Godot version

4.2 bc88dca

System information

Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1060 (NVIDIA; 30.0.15.1403) - Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 Threads)

Issue description

After #80859 if you have an action that uses joypad axis, it can get stuck very easily and don't get released until more input. The pressed counter can go above 1, because multiple pressed events are received in a row, without equivalent release events that would release the action.

Steps to reproduce

  1. Create an action that uses joypad axis event
  2. Start the project and press the action a few times
  3. Eventually it will not release

Minimal reproduction project

BrokenAxis.zip

@YuriSizov
Copy link
Contributor

cc @Sauermann

@Sauermann
Copy link
Contributor

Without a joypad I am having trouble replicating this issue. Not sure, how I can help with this. :-/

@miv391
Copy link
Contributor

miv391 commented Aug 30, 2023

I cannot replicate this.

System: Godot v4.2.dev3 - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1060 6GB (NVIDIA; 31.0.15.3640) - Intel(R) Core(TM) i5-3570K CPU @ 3.40GHz (4 Threads)
I tested with Xbox360 and Nintendo Switch controllers.

In the reproduction project the action is tied to all devices. Could that cause some problems if there are more than one controller connected? With that "All devices" action only my number 1 controller works (Nintendo), controller 0 (Xbox360) doesn't work. I need to tie the action only to devide 0 or 1 to be able to use a certain controller.

@KoBeWi
Copy link
Member Author

KoBeWi commented Aug 30, 2023

I have only one controller connected (Xbox One S).

@BlueCube3310
Copy link
Contributor

BlueCube3310 commented Aug 30, 2023

Can confirm (Windows 10, PS4 controller via DS4Windows), by wiggling the control stick to the right without recentering it the input gets stuck as pressed.

@theraot
Copy link
Contributor

theraot commented Sep 1, 2023

I have code that emulates a joystick using parse_input_event (so in my game I don't need two code paths for handling aiming, one for mouse and one for joystick - emulating the mouse form joystick is less viable). My code sends pressed events to change the strength, as a result it also gets stuck like the actual joystick (yes, I also tested with an actual joystick). I had no issue in Godot 4.2 dev 2 (emulated joystick was working fine, and also actual joystick was working fine). Edit: Godot 4.2 dev 3 was fine too.

This is my code for joystick emulation:

extends Node


@export var left:String = "joy_look_left"
@export var right:String = "joy_look_right"
@export var up:String = "joy_look_up"
@export var down:String = "joy_look_down"


var mouse_input:Vector2


func _input(event: InputEvent) -> void:
	if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
		return

	var type_found := false
	var relative:Vector2
	var screen_drag := event as InputEventScreenDrag
	if screen_drag != null:
		relative = screen_drag.relative
		type_found = true

	var mouse_motion := event as InputEventMouseMotion
	if mouse_motion != null:
		relative = mouse_motion.relative
		type_found = true

	if not type_found:
		return

	mouse_input = relative / 100.0
	set_process(true)


func _process(_delta: float) -> void:
	if left != "" and right != "":
		var value := clampf(Vector2(1.0, 0.0).dot(mouse_input) * 2.0, -1.0, 1.0)
		_set_axis(left, right, value)

	if up != "" and down != "":
		var value := clampf(Vector2(0.0, 1.0).dot(mouse_input) * 2.0, -1.0, 1.0)
		_set_axis(up, down, value)

	if mouse_input.is_zero_approx():
		set_process(false)
		return

	mouse_input = Vector2.ZERO


func _set_action(action:String, strength:float) -> void:
	var pressed := strength > 0.0
	if (
		Input.is_action_pressed(action) != pressed
		or Input.get_action_strength(action) != strength
	):
		var event := InputEventAction.new()
		event.action = action
		event.pressed = pressed
		event.strength = strength
		Input.parse_input_event(event)


func _set_axis(negative_action:String, positive_action:String, value:float) -> void:
	_set_action(negative_action, maxf(-value, 0.0))
	_set_action(positive_action, maxf(+value, 0.0))

I tried releasing the event in a loop as workaround, but that seems to be an infinite loop.

@hunterloftis
Copy link
Contributor

hunterloftis commented Sep 5, 2023

In case anyone else is currently working around this issue, I've had success by implementing analog ("joystick") input at a lower level, via:

  • Input.get_joy_axis
  • Input.parse_input_event

For example, to work around how this issue impacts Input.get_vector, I'm using:

func _update_directions() -> void:
	move_direction = _fixed_get_vector(JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y).normalized()
	look_direction = _fixed_get_vector(JOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y, LookMagnitude2)

# workaround https://github.com/godotengine/godot/issues/81164
func _fixed_get_vector(horizontal: JoyAxis, vertical: JoyAxis, dead_squared := DeadZone2) -> Vector2:
	var vector := Vector2(Input.get_joy_axis(device, horizontal), Input.get_joy_axis(device, vertical))
	return vector if vector.length_squared() >= dead_squared else Vector2.ZERO

And to work around how this issue plus others in Godot 4 impact trigger-based actions, I'm using:

# workaround https://github.com/godotengine/godot/issues/81164
# and https://github.com/godotengine/godot/issues/72636
var _trigger_actions = {
	"dash": false,
	"use": false,
}

func _read_triggers() -> void:
	_read_trigger("dash", JOY_AXIS_TRIGGER_LEFT)
	_read_trigger("use", JOY_AXIS_TRIGGER_RIGHT)

func _read_trigger(action: String, axis: JoyAxis) -> void:
	var trigger := Input.get_joy_axis(device, axis)
	if trigger >= DeadZone and not _trigger_actions[action]:
		_trigger_actions[action] = true
		_simulate_action(action, true)
	elif trigger <= DeadZone and _trigger_actions[action]:
		_trigger_actions[action] = false
		_simulate_action(action, false)

func _simulate_action(action: String, pressed: bool) -> void:
	var event = InputEventAction.new()
	event.action = action
	event.pressed = pressed
	event.device = device
	Input.parse_input_event(event)

@Cronos87
Copy link

Cronos87 commented Sep 11, 2023

I can confirm this issue on macOS with Godot 4.2.dev4 using a PS5 controller. It works well on the last version of 4.1.

Using the D-pad, pressing buttons will work for 1 second and then be stuck. Regarding the joystick, if I use it, the vector2 I get from get_axis() will never come back to 0 again.

@chriscamacho
Copy link

I'm seeing this when using a key that is also on half an axis, works fine on a system using a joystick, but on a system without a joystick just using the key it will get stuck from time to time, no matter how many times the key is pressed or the key on the other side of the axis, the axis is stuck on left...

@naturally-intelligent
Copy link

I'm now getting this in 4.3 beta 1, in dev5 and below it is fine

Holding arrow key down, then releasing, never gets released in engine, and always is pressed, rendering game unplayable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants