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?
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.
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.
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.
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.
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)
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.
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.