Input.is_action_pressed() doesn't recognize button that was already pressed

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Godont

I have a state machine to handle states of my player. When the player is in the “StateStunned” state, a timer counts down and then transitions to the “StateIdle” state once the timer finishes. In the “StateIdle” state input is handled, and if the left or right buttons are pressed the player state transitions to “StateMoveHorizontal”:

func handle_input(_event : InputEvent):
	left_pressed = Input.is_action_pressed("ui_left")
	right_pressed = Input.is_action_pressed("ui_right")

func physics_update(delta: float) -> void:

    if left_pressed or right_pressed:
		state_machine.transition_to("StateMoveHorizontal")
		return

The code works great except if the player was already holding left or right when stunned and kept holding it. Then, the transition from “StateIdle” to “StateMoveHorizontal” will only happen if the player “let’s go” of left or right then presses it again.

Is the issue that the input event has gotten consumed elsewhere or something? Is this a bug? How can I handle this?

Thanks!

From the code you provided, are the variables left_pressed and right_pressed members of the state itself? I have the same state machine setup, but the player input variables are members of the player itself instead of states, and it works fine. In my case, it’s a roll state that locks the player input movement, and once it ends it transitions to the Idle state, again capturing the input (as seen here).

Joel Gomes da Silva | 2023-03-30 13:27

So I followed the approach outlined in GD Quest’s tutorial on finite state machines. There they handle input within each state with the handle_input(_event : InputEvent) function, which is called by the state machine itself. Would this approach lead to the issue I’m having? Is the issue that the input is getting consumed by some states so that others cannot use it?

Godont | 2023-03-30 14:14

I think the issue may be due to in a frame the input is pressed, you transition to another state, then in the next frame it’s already pressed on the previous frame, so no other event will be emited to _input. If you really don’t want the input variables in the player, try checking for the input on the physics_update, since it will surely run the input checking on all frames.

Joel Gomes da Silva | 2023-03-30 15:28

:bust_in_silhouette: Reply From: Godont

So for whatever reason, it is definitely the case that under certain circumstances you can be holding right or left on your analog stick (which are mapped to say “ui_right” and “ui_left” with a deadzone of 0.25) and yet have Input.is_action_pressed("ui_right") and Input.is_action_pressed("ui_left") return false on every frame/update of the game loop. The circumstances that gave rise to this for me are transitioning to a new state in my player’s state machine with the joystick already held in one direction. Releasing and holding again makes it work, but I can’t tell if this is a bug or intended.

To get things working I abandoned Input.is_action_pressed and switched to Input.get_joy_axis:

var xAxis = Input.get_joy_axis(0,JOY_AXIS_0)
if abs(xAxis) > 0.2:
	right_pressed = xAxis > 0.2
	left_pressed = xAxis < -0.2

I will have to add code later to check whether there is a controller connected and to handle other forms of input, but for now this works.

If anyone believes this is a bug (or if I should go about this another way) please let me know and I can try to log it if it isn’t already.

:bust_in_silhouette: Reply From: aidave

Try moving that code in handleinput into your process call instead.