What could be the reason gamepad button presses and releases aren't received by the game sometimes?

Godot Version

v4.5.1.stable.official [f62fdbde1]

Question

I’m making a 2D flying game where you can use the LB and RB buttons on a controller to turn left and right (as well as keyboard keys and mouse buttons). I’ve defined two input actions for this with all of the relevant controls assigned to them.

When I test this with my Xbox controller on my main Windows computer, every once in a while a button press or release is not received by the game (and as a result I crash into something). I haven’t been able to find any reasons for it to happen so far, for example CPU and GPU usage seem fine at first glance.

I have not had this happen with another controller (a Logitech Rumblepad), keyboard keys or mouse buttons. It also hasn’t happened yet if I use the Xbox controller on another computer (either Windows or Linux). However, a friend has this problem with keyboard and mouse on his Linux computer.

I have tried using Input.is_action_just_pressed in both _process() and _physics_process(), as well as using events in _input(). I tried the latest beta version of Godot 4.6 as well to no avail.

Does anyone happen to have any idea what the cause of this might be?

Really hard to say without more detail. Keyboards can ghost, worth testing on a site like this Multi Key Display Test; but controllers are typically wired in a way that prevents ghosting, and Godot does a good job reading inputs. You may need to test more thoroughly with the controller, what buttons don’t press/release? How consistent is it? Is there any software intercepting your inputs, like steam?

What’s your code actually look like? That could be the issue.

I realize there’s not much to go on.

  • It happens very rarely, though often enough to notice.
  • It happens without Steam running and I don’t think anything else is using the controller.
  • So far I’ve only noticed it with the LB and RB buttons myself.

I added this code to see what buttons are pressed while playing the game:

func _input(event) -> void:
	if event is InputEventJoypadButton:
		var e := event as InputEventJoypadButton
		var c: Color = Color(Color.WHITE * maxf(0.5, int(e.pressed)), 1.0)
		match e.button_index:
			9: %LB.modulate = c
			10: %RB.modulate = c
	elif event is InputEventMouseButton:
		var e := event as InputEventMouseButton
		var c: Color = Color(Color.WHITE * maxf(0.5, int(e.pressed)), 1.0)
		match e.button_index:
			1: %LeftButton.modulate = c
			2: %RightButton.modulate = c
	elif event is InputEventKey:
		var e := event as InputEventKey
		var c: Color = Color(Color.WHITE * maxf(0.5, int(e.pressed)), 1.0)
		match e.physical_keycode:
			Key.KEY_LEFT: %Left.modulate = c
			Key.KEY_RIGHT: %Right.modulate = c
			Key.KEY_A: %A.modulate = c
			Key.KEY_D: %D.modulate = c

This doesn’t use the actions I defined, but it has the same problem: the visuals don’t change when the problem occurs.

The code that actually processes inputs while playing looks like this:

func ProcessInput(delta: float) -> void:
	var inversion: int = 1 # not inverted
	if Settings.InvertControls:
		inversion = -1 # inverted
	#
	if Input.is_action_pressed("rotate_up"):
		RotationState = Rotation.UP
		Game.ShipAngle = Game.ShipAngle - inversion * (_CalculateRotationSpeed() * delta)
	if Input.is_action_pressed("rotate_down"):
		RotationState = Rotation.DOWN
		Game.ShipAngle = Game.ShipAngle + inversion * (_CalculateRotationSpeed() * delta)
	Game.ShipAngle = fmod(Game.ShipAngle, 360)
	#
	if !Input.is_action_pressed("rotate_up") && !Input.is_action_pressed("rotate_down"):
		_StopRotation()
	pass

Ok, so my initial comment on your first block of code is that the code is overly complicated for the sole reason of obfuscating variable names. You are literally creating a dereference to the event variable causing it to jump through another memory address before doing anything with it. This is not likely the cause of your issue, but it could be contributing. How come you are doing this?

Second thing I notice is you are using PascalCase for function names and variable names instead of snake_case. I recommend you read the GDScript Style Guide. Choices like that make it look like you are using a Enums everywhere which makes it harder to process your code.

Third thing - you don’t need pass at the end of functions. It doesn’t do anything.

What’s the code for _CalculateRotationSpeed() and _StopRotation() and what’s your scene tree look like?

Ok, so my initial comment on your first block of code is that the code is overly complicated for the sole reason of obfuscating variable names. You are literally creating a dereference to the event variable causing it to jump through another memory address before doing anything with it. This is not likely the cause of your issue, but it could be contributing. How come you are doing this?

I’m used to type safe languages (Delphi, C++), so I convert a variable to a specific type before I use it. It also helps in the editor as it then shows me the relevant properties in code complete.

Second thing I notice is you are using PascalCase for function names and variable names instead of snake_case. I recommend you read the GDScript Style Guide. Choices like that make it look like you are using a Enums everywhere which makes it harder to process your code.

I understand, not ideal when sharing code indeed. I’m slowly adapting to the GDScript style, though I don’t think I ever will completely (not a big fan of underscores in names, tbh).

Third thing - you don’t need pass at the end of functions. It doesn’t do anything.

I got out of that habit for newer code already, but the second snippet is still an old function.

Here are screenshots of the trees of the game scene and the ship scene.

The game scene is added to the tree in code with get_tree().change_scene_to_file().

func _CalculateRotationSpeed() -> float:
	return MOVE_SPEED_CONST * Game.ShipSpeed * Game.ShipRotation

func _StopRotation() -> void:
	_SnapAngleToBigFour()
	Game.ShipAngle = Game.ShipAngle_Snapped
	RotationState = Rotation.OFF
	pass

Fwiw, I did already test without _SnapAngleToBigFour() and it still happens in that case, so that’s unlikely to be involved.

Here’s a short video of what happens:

In this test I alternately press the LB and RB buttons on my controller. When the ship goes in a straight line at the end, I pressed the RB button but the game didn’t see that.

K.

if event is InputEventJoypadButton:

In any code that is indented under this line event will be treated like an InputEventJoypadButton and give you autocompletion in the editor.

From a C++ point of view, you are creating an extra pointer just so you can dereference it when you use it. This increases your memory usage and slows down your processing 60 times a second. It’s not really an issue to performance, but I thought I’d mention it because based on your reasons, it isn’t necessary.

Plus, as mentioned you are using a one-letter variable. If that works for you, cool, but if you want to work with others it’s just frustrating. This isn’t 80s C programming - the program doesn’t run faster and with less memory if your variable name is shorter.

Whatever works for you. But keep in mind when you post here you are asking people just like you to volunteer their time and spend their time decoding your code. Sounds like you get that, but it’s a good reminder for others reading this thread later.

I’m sorry you don’t like underscores. But you should know that if any of your assets have capital letters in them and you export to Windows those assets will not show up in your game. It’s a frustrating experience. There’s a number of gotchas like that, which is why I just suggest to new people they follow the guide. Saves a lot of frustrations later on.

Thoughts

So I started coding you a different solution altogether (pulled from my Camera3D plugin rotation code), and then as I was copying things to match your code. That’s when I noticed that you set RotationState = Rotation.OFF in _StopRotation(). When do you turn it back on? My best guess is you are turning it off and I’m wondering if you have some code that’s somehow catching that and preventing you from assigning states of UP or DOWN to it thereafter.

Are you bumpers considered the same axis on the controller? I know D-Pads can be registered as the same axis on some devices, my logitech F310 (at least on Linux in it’s Xinput mode) combines the D-Pad buttons into axis 6 and 7, so pressing left and right would register zero on that axis (though physically impossible on the joined D-Pad). Maybe your bumpers are presenting as a combined axis?

I honestly doubt this theory since it doesn’t happen on other games/programs as you state, and Godot seems to detect your shoulder buttons as JoyButton based on your sample here:

Real hail-mary idea, I’m fairly certain it’s not Godot, or not Godot directly; from my understanding Godot is heavily influenced by SDL2’s input which has decades of near complete market dominance in games. If this is truly Godot specific it should be submitted as an issue, I believe your code is fine and should function with functional equipment.

1 Like

RotationState is turned on in ProcessInput(). It’s not related to the problem however, as it also happens in (much) older versions of the code that don’t have this.

I’ll probably try coding just the flying from scratch in a new project to see if I can get it to happen in another project as well. That may give me a clue as to what specific code causes it, if any.

1 Like

As far as I can tell they’re used as buttons, not on an axis.

I agree it’s unlikely that this is because of Godot, though I haven’t been able to pin it on any specific code in my game either.

I’ve ordered a new Xbox One controller to see if it’s specific to the one I have now (but only on this computer) or not.

I’ll try that out, it would be easier and less code so that would be good.

Fwiw, I’m going to leave the discussion about performance and code style here. You make good points and I’ll keep them in mind.

1 Like

Give me 10 min. I might have a simpler solution for you.

1 Like

Since you’re starting over, here’s the part I didn’t post before:

There’s a lot going on in your code. TBH I just don’t want to spend time going through all the math you’re doing. I think it’s mostly unnecessary. Instead, I’m going to suggest something simpler. First, the code I’m adapting this from, which is my Camera3D plugin. It uses my Controller plugin to capture the input.

controller.gd

## Stores the amount of movement in the x/y direction that the player is trying
## to look in a 3D game. The export variable enable_3d_look must be set to true
## for this to have any value. (See dragonforge-character-3d for usage example.)
var look := Vector2.ZERO

gamepad.gd

func _unhandled_input(event: InputEvent) -> void:
	if Controller.enable_3d_look and event is InputEventJoypadMotion:
		if event.axis == JOY_AXIS_RIGHT_X:
			Controller.look = Vector2(-event.axis_value * horizontal_look_sensitivity, Controller.look.y)
		if event.axis == JOY_AXIS_RIGHT_Y:
			Controller.look = Vector2(Controller.look.x, -event.axis_value * vertical_look_sensitivity)

camera_mount_3d.gd

func _physics_process(delta: float) -> void:
	_update_rotation()

func _update_rotation() -> void:
	horizontal_pivot.rotate_y(Controller.look.x)
	vertical_pivot.rotate_x(Controller.look.y)
	vertical_pivot.rotation.x = clampf(vertical_pivot.rotation.x,
		deg_to_rad(upwards_rotation_limit),
		deg_to_rad(downwards_rotation_limit)
	)
	_apply_rotation()

func _apply_rotation() -> void:
	spring_arm_3d.rotation.y = horizontal_pivot.rotation.y
	camera_3d.rotation.x = vertical_pivot.rotation.x

Scene Tree

Suggestion

This code may need some tweaking to fit variable names, etc in your game. (E.G. it assumes that “rotate_up” is negative and “rotate_down” is positive.) But I did test it my XBox controller using this as the code for a CharacterBody2D and it works fine. I mapped the left bumper to “rotate_up” and the right to “rotate_down”. You can change the exported variable while the game is running to tweak the turn speed.

extends CharacterBody2D

@export var rotation_sensitivity: float = 5.0


func _physics_process(delta: float) -> void:
	var rotate_direction = Input.get_axis("rotate_up", "rotate_down")
	rotation += rotate_direction * rotation_sensitivity * delta

Thanks. I’ll take a look at this tomorrow.

1 Like

I just tested my new Xbox One controller (and a different cable just in case), and it still happens. So it’s unlikely to be caused by the hardware.

Make a super simple test in a fresh project that only prints the events and see if it happens there. If yes, it’s Godot and you should report it. If not, it’s your project, continue to debug it.