godot: Movement doesn't work as expected when mapping keys and axes to the same action


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


Godot version: 3.2.4.rc1.official

OS/device including version: Windows 10 (19042.746)

Issue description: When I plug in a PS4 controller, KinematicBody2D doesn’t move properly while using the d-pad buttons or the keyboard. Same occurs if I use DS4Windows to emulate a Xbox 360 controller (XInput).

(!) Note The same code works fine in Godot v3.2.3.stable, both for the PS4 controller (with and without DS4Windows) and the keyboard.

func _physics_process(delta):
	velocity = get_direction() * speed
	velocity = move_and_slide(velocity)


func get_direction():
	var dir: = Vector2.ZERO
	dir.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
	dir.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up")
	dir = dir.normalized()
	return dir

Steps to reproduce:

  1. Open and run the attached project with a PS4 controller connected.
  2. Move the rectangle on the screen using WASD or the arrow keys, then try to move it using the d-pad buttons.
  3. The node’s movement shouldn’t work properly, neither with the keyboard or the controller.
  4. Disconnect the controller and the rectangle will move without problems using the keyboard.

Repeat steps 1 and 2 in Godot v3.2.3.stable.official and the rectangle will move without problems.

Here you can see how the gamepad registers the buttons pressing without moving the rectangle, same for WASD and arrow keys.

Minimal reproduction project:

GamepadInputError.zip

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 4
  • Comments: 48 (16 by maintainers)

Commits related to this issue

Most upvoted comments

Same issue here.

Godot v4.1.dev4 (C#).

Mapped the action move_left to the A key and left joystick to the left. Mapped move_right to D and left joystick to the right.

Using Input.GetAxis("move_left", "move_right") while playing with a keyboard sometimes returns 0 and makes the character stop for a fraction of a second before moving again. Playing with the controller works fine. Disconnecting the controller fixes the issue with the keyboard.

This issue was not occurring before I mapped the actions to the joystick (so using only the keyboard works fine)

@madmiraal Hi, sorry for the late response and for not making clear what’s going on.

| Expected | What it does in 3.2.3 | What it does in 3.2.4.rc1 – | – | – | – Doing Nothing | Nothing | Nothing | Nothing W pressed | Character moves up continiously | Works as expected | Character moves up and suddenly stops moving A pressed | Character moves left continiously | Works as expected | Character moves left and suddenly stops moving S pressed | Character moves down continiously | Works as expected | Character moves down and suddenly stops moving D pressed | Character moves right continiously | Works as expected | Character moves right and suddenly stops moving D-pad Up | Character moves up continiously | Works as expected | Character moves up and suddenly stops moving D-pad Left | Character moves left continiously | Works as expected | Character moves left and suddenly stops moving D-pad Down | Character moves down continiously | Works as expected | Character moves down and suddenly stops moving D-pad Right | Character moves right continiously | Works as expected | Character moves right and suddenly stops moving

Same occurs for diagonals (pressing two buttons at the same time).

Mapping multiple keys or a key and an axis to a single action is known to cause issues: see #30888 and #39287. #30890 updated the documentation for is_action_pressed() to highlight this, but the same applies to get_action_strength(), which is what is happening here. This particular scenario was less evident in 3.2.3, because small movements were been filtered out (which was causing #42876, and fixed with #43233 and backported to 3.2 with #43234) but as identified in #39287, it is a pre-existing problem that doesn’t have an easy fix.

@bojidar-bg suggested, changing the behaviour of is_action_pressed() to not release until all bound buttons are released. This could be extended to get_action_strength(), by using, for example, the maximum of multiple buttons and axes.

Note that this current behavior also causes issues in configurations where your keyboard is configured to send both key and gamepad events. I use a Wooting two HE analog keyboard, with the analog profile configured to send both key presses and left stick analog movement on the arrow keys. This allows using the same profile for general desktop usage and games that make use of analog movement.

This analog profile works great in games like Trackmania (even if steering is both bound to arrow keys and gamepad axes in-game). Unfortunately, it doesn’t work as expected in Godot since the key and gamepad input will “fight” each other if both are configured for the same input action.

Testing project: test_analog_and_digital_input.zip

The top (white) progress bar represents the input that accepts both digital and analog input (with both inputs being sent from the keyboard). The middle (yellow) progress bar represents gamepad input only (separate action). The bottom (cyan) progress bar represents keyboard input only (another separate action).

The default deadzone is used for all inputs.

Here, I’m slowly pressing the key then depressing it:

https://user-images.githubusercontent.com/180032/137774927-79d72c26-d7fe-44e9-9956-54cbd1b75b4b.mp4

Calling Input.set_use_accumulated_input(false) in _ready() doesn’t resolve this issue.

Nice. Will this fix end up in 3.6 too at some point?

Godot 4.0 beta 10. Seeing the same issue with the Steam Deck controller when you have d-pad and joystick input mapped to the same action.

For others who run into this, here is my workaround:

For player input handling, the following picks the greater of button or analog input:

func get_vector_for_input_prefix(prefix) -> Vector2:
	return Input.get_vector(prefix + "move_left", prefix + "move_right", prefix + "move_up", prefix + "move_down")

func _physics_process(delta):
	# Workaround bug when mapping both buttons and analog inputs to a single event:
	#
	#    https://github.com/godotengine/godot/issues/45628
	#
	# If the bug is fixed, switch the below block of code to simply:
	#
	#   var input: Vector2 = Input.get_vector("left", "right", "up", "down")
	#
	var input: Vector2 = Vector2.ZERO
	var analog_input: Vector2 = get_vector_for_input_prefix("analog_")
	var button_input: Vector2 = get_vector_for_input_prefix("button_")
	if analog_input.length() > button_input.length():
		input = analog_input
	else:
		input = button_input

	velocity = input * SPEED_PIXELS_PER_SEC / delta
	move_and_slide()

The input map looks like this: Screenshot 2023-08-17 at 1 20 34 PM

Keywords to help others find this issue and workaround: Input.get_vector, Input.is_action_pressed, joystick, buttons, character movement

I have this issue with a PlayStation DualShock 4 controller on both a Windows system and a macOS system, with Godot 4.1.1. The reproduction was the same as others are seeing here: buttons randomly stop reporting being pressed. Since I am new-ish to Godot, it wasn’t obvious whether I was doing something wrong or if the issue was with the engine. Unfortunately, I lost half of a vacation day to tracking down the root issue (combining buttons and joystick inputs). I got to learn a bit about Godot in the process, so it wasn’t entirely wasted, but I can’t imagine the Godot maintainers want new users to go through this frustrating experience.

If the Godot maintainers are not ready to merge the proposed patch, please consider adding a warning in the Input Map dialog that says “Combining Joystick and Button presses in the input map is not supported: see issue #45628”; or disable combining buttons with joystick axes entirely. The warning/error would have saved frustration; and I can see from the comments on this issue it would help others as well.

is usually a sign of broken device and/or drivers, so it’s alarming to hear that Steam Deck exhibits that behavior (it’s too new to be a hardware fault)

@Zireael07, yea I was surprised to see it. A user reported the associated behavior to me so I bought a Steam Deck and saw it on my own as well. The odd thing is joystick deadzones can be set & tested on a Steam Deck–when I tested mine I saw no visible jitter or movement outside the default deadzones. Worth noting again that I saw the “flurry” only after any button on the deck is engaged; until then it is silent. My gut intuition says that it’s likely a software meets hardware issue on the Steam Deck’s side, closely associated with behavior of the “Steam Virtual Gamepad”.

Where might such an issue be reported? I assumed noting the behavior here was sufficient enough, given Valve stated they “talk to Godot” 😂: https://partner.steamgames.com/doc/steamdeck/faq

@joemicmc, I was recently hunting this issue with my game and someone playing it through a Steam Deck. Fortunately (unfortunately), someone else reported similar behavior with a different device entirely. Through testing, the different device was issuing axis events along with dpad events.

But from what I can tell with the Steam Deck’s virtual controller, once it’s engaged (at least with Godot) it fires off a continuous flurry of axis events at close to 0.0 levels. I believe this behavior is a mistake in the Steam Deck (hardware or software–I don’t know). The resulting behavior is that even if you are listening for action events via _input, even if you are just using dpad, unwarranted axis events most likely registering under the action’s dead zone will “unpress” the behavior you may have associated with dpad. And depending on how things are engineered, wonky behavior can ensue.

What does this have to do with the “different device”? Well, I had to tighten up my input processing to be robust enough to deal with both nonsensical & unexpected behavior. An easy & less ideal way out is to decouple the game pad axis from the action associated with your dpad. I’d recommend doing that unless you really need both to work.

https://www.reddit.com/r/godot/comments/v9bizt/comment/ic4ik6x/

Potentially to solve this issue could the input mapping have like an event priority per suggestion of workaround in this reddit thread.

“I have found a solution that seems to work OK… I capture only the D-pad input vector, however if that’s Vector2.ZERO then I’ll capture the analogue stick motion and use that. So the D-pad is the dominant one, and the analogue stick is the fall back. Only issue might arise in a game where both left d-pad and left stick need to be used together, but I only have one left thumb so unlikely(? 😃”

a very niche problem that can cause issues with having a PS4 controller plugged in and trying to switch from one to the other, IE if you have both WASD and mouse enabled and also a controller it will cause issues. This can be avoided by having a menu option to switch between settings, but alot of if not the majority of games now implement a way to switch fluidly between the 2 with out them affecting each other. Not sure what causes this interaction.

Is there a way to ignore joystick inputs besides removing them from the input mapping?

There’s currently no way to disable controller support entirely, but I suppose that will need to be added eventually (e.g. with a project setting and --no-gamepad CLI argument).

Yes @madmiraal . The problem disappears if I remove the axis mappings. 😄