Double and triple inputs on controller

Godot Version

4.5.1 stable Windows 11

Question

I’m trying to implement controller UI navigation in my game, testing with a Dualshock 4 controller, but there’s a problem where sometimes my inputs happen twice, or even thrice.

For example, when you select the “start” button in the main menu, it’s supposed to take you to the character select screen and create an option for every character entry in the dictionary. But sometimes, seemingly at random, it creates each option twice, or even thrice.

Another example, when selecting the settings, it goes to the settings screen and immediately selects the first focused option.

This is the code I have on my menu button script:

	if Input.is_action_pressed("confirm") and not event.is_echo():
		if mouse_over && can_be_selected :
			print("confirm input")
			emit_signal("button_selected", button_type)

It prints “confirm input” sometimes once, sometimes twice, and sometimes thrice. I’ve tried using is_action_just_pressed, is_action_pressed, with and without not event.is_echo(), but the result is always the same.

And this has also happened once with mouse inputs, but only one time in my entire playtest history.

What’s the problem here? Is it a Godot problem, or a problem with my controller? If it’s a controller problem, how can I failsafe my game against double inputs? Any help is appreciated.

The problem is likely a bug in your code. Post the entire script.

Also make sure you’re not running the same script on multiple nodes. Put print(get_path()) right beside `print(“confirm input”) to check if it’s coming from the same node.

get_path() returns just the one node, even when getting multiple inputs.

This is the entire script:

extends TextureButton


var mouse_over : bool = false
var can_be_selected : bool = true
@onready var hover_snd : AudioStreamPlayer = $"../../button_hover"
@onready var hover_snd2 : AudioStreamPlayer = $"../button_hover"
@onready var select_snd : AudioStreamPlayer = $"../../button_select"
@onready var select_snd2 : AudioStreamPlayer = $"../button_select"
@export var button_type : String

func _ready() -> void:
	pivot_offset = size / 2

signal button_selected(button : String)

func _input(event : InputEvent) -> void:
	if Input.is_action_just_pressed("confirm") and not event.is_echo():
		if mouse_over && can_be_selected :
			print("confirm input")
			emit_signal("button_selected", button_type)
	if Input.is_action_pressed("back") :
		if button_type == "backcharacter" or button_type == "backstage" :
			emit_signal("button_selected", button_type)
			

func _on_mouse_entered() -> void:
	if can_be_selected :
		if not hover_snd == null :
			hover_snd.play()
		else :
			hover_snd2.play()
		$AnimationPlayer.play("hover")
		z_index += 2
		mouse_over = true

func _on_mouse_exited() -> void:
	$AnimationPlayer.play("RESET")
	z_index -= 2
	mouse_over = false


func _on_focus_entered() -> void:
	if can_be_selected :
		if not hover_snd == null :
			hover_snd.play()
		else :
			hover_snd2.play()
		$AnimationPlayer.play("hover")
		z_index += 2
		mouse_over = true

func _on_focus_exited() -> void:
	$AnimationPlayer.play("RESET")
	z_index -= 2
	mouse_over = false

Don’t use is_action_pressed(). In fact don’t use Input in the _input() handler at all. You can get all the needed info from the event object.