Button Input on pause

Godot Version

4.5.1 stable

Question

So i’m making a pause menu and I use get_tree().paused to pause the game, i have the process mode to always so my animation from the menu work but the Input doesn’t. Yes the closing and opening Input escape works because it’s unhandled :+1:

But the buttons themselves don’t gain any hover, focus or press. I’m trying to figure out how to make them work, but didn’t find any answers in the docs :upside_down_face:

This will make everything under the main tree paused, unless you set “Process Mode” to Always in the nodes, instead of Inherited.

So if you make your main Pause Menu Control Node “Process Mode” to Always, the buttons that are child of it should work as expected.

When running your game, go to the Debugger → Misc and check what Control Node you are clicking.

Sometimes it happens that you have an empty Control node, or something fully transparent stretched over the whole window, that blocks the input.

I already had the process mode on always, I just cant get Input on the buttons themselves, as if they’re not unhandled

I got this but it only happens when I click, not when use arrow keys

And is this the button you expect to see clicked when you click?

yes, but I also forgot to say, I cant really hover it, only clicking activates it? I’m at school right now, so I can give you more details after 2 hours:)

If that’s what you wanted then what’s the problem? I’m confused now.

Do you just want to change how it looks when you hover? You can change that in the Button’s theme

Try to test your scene in an isolation and get it working isolated. Then once you get it working there, incorporate that into your main scenes. If it works in isolation and doesn’t work when incorporated into the scenes, then the problem is somewhere else.

How are you trying to get input? Are you hooking a signal up in the button’s signal handlers, or are you putting your input handling in script in a node that’s being disabled, perhaps?

My apologies, so when clicking it gets activated on the debugger, not in game. so nothing actually happens. I have an indicator sprite that moves to the position to where you’re hovering or focusing, but it stands still even tho it’s a child of the node that has it’s process mode on always and thread inherhited. I can’t really shoz you anything because im at school but in a few minutes ill be clarifying my problim with button Input not getting detected

I have a class called navigation. it focuses the main button. you can navigate by hovering or focusing, if a hover or focus is detected then a signal gets called which ill be showing soon, it checks which button has been detected and add it’s stuff. and no, they work perfectly because I tested them before using get_tree().paused = true

For people who are seeing this. I still don’t know the answer. But I have a class called Navigation that extends Panel, here is it’s script

extends Panel
class_name Navigation

@onready var vbox_container: VBoxContainer = $VBoxContainer
@onready var indicator: Control = $Indicator

@export var arrow_offset: int = 48
@export var main_button: Control

signal button_pressed(button: Button)

func _ready() -> void:
	process_mode = Node.PROCESS_MODE_ALWAYS
	if main_button:
		pressed(main_button)
		await get_tree().create_timer(0.5).timeout
		main_button.grab_focus()
	for button in vbox_container.get_children():
		if button is Button:
			button.mouse_entered.connect(enter_focus.bind(button))
			button.focus_entered.connect(enter_focus.bind(button))
			button.pressed.connect(pressed.bind(button))

func _unhandled_input(event: InputEvent) -> void:
	get_viewport().set_input_as_handled()

func enter_focus(button: Button) -> void:
	var arrow_right: Sprite2D = indicator.get_child(0)
	var arrow_left: Sprite2D = indicator.get_child(1)

	var target_y: float = button.global_position.y

	# Font + size
	var font: Font = button.get_theme_font("font")
	var font_size: int = button.get_theme_font_size("font_size")

	# Text width
	var text_width: float = font.get_string_size(button.text, font_size).x

	# Make the arrow distance dynamic but with a minimum offset
	var dynamic_offset: float = max(arrow_offset, text_width * 0.3)

	# Arrow local positions
	var left_x: float = -(text_width / 2 + dynamic_offset)
	var right_x: float = (text_width / 2 + dynamic_offset)

	# Tween
	var tween: Tween = create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_CUBIC).set_parallel()

	tween.tween_property(indicator, "global_position:y", target_y, 0.15)
	tween.tween_property(arrow_left, "position:x", left_x, 0.15)
	tween.tween_property(arrow_right, "position:x", right_x, 0.15)

func pressed(button: Button) -> void:
	$Indicator/GPUParticles2D.restart()
	button_pressed.emit(button)

I also have a custom script added to my navigation node that handles the menu:

extends Navigation

@onready var main_anim: AnimationPlayer = $“../Main”

var menu_path: NodePath = “res://Scenes/Menus/Main Menu/main_menu.tscn”
var paused: bool = false:
set(value):
paused = value
if paused:
main_anim.play(“Pause”)
$VBoxContainer/Resume.grab_focus()
get_tree().paused = true
get_viewport().set_input_as_handled()
else:
main_anim.play_backwards(“Pause”)
$VBoxContainer/Resume.grab_focus()
$VBoxContainer/Resume.release_focus()
get_tree().paused = false

func _ready() → void:
process_mode = Node.PROCESS_MODE_ALWAYS
$“..”.process_mode = Node.PROCESS_MODE_ALWAYS

func _unhandled_input(event: InputEvent) → void:
if event is InputEventKey:
if event.pressed and event.keycode == KEY_ESCAPE:
paused = !paused

func _on_button_pressed(button: Button) → void:
if button == $“VBoxContainer/Quit To Menu”:
load_menu()
elif button == $VBoxContainer/Resume:
paused = false

func load_menu() → void:
var fade_tween: Tween = create_tween().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE).set_parallel()

$"../Loading Screen".show()
fade_tween.tween_property($"../Loading Screen", "self_modulate:a", 1.0, 0.25)
# Start threaded load
if !ResourceLoader.has_cached(menu_path):
	ResourceLoader.load_threaded_request(menu_path)

# Wait until loading is fully done
await _wait_for_threaded_load()

# Now safely retrieve the resource
var packed_scene: PackedScene = ResourceLoader.load_threaded_get(menu_path)

# Change scene AFTER loading is fully done
get_tree().change_scene_to_packed(packed_scene)

func _wait_for_threaded_load() → void:
while true:
var status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status(menu_path)
if status == ResourceLoader.THREAD_LOAD_LOADED:
return
elif status == ResourceLoader.THREAD_LOAD_FAILED:
push_error(“Failed to load scene!”)
return
await get_tree().process_frame

Now I’m going to show you my scene tree (Menu ectends Navigation)

Now I will show you how it looks in game Im opening it and closing it with escape because that’s the only thing that works:

It’s kind of a weird thing and I’m sure it has to do with how Input is handled in Buttons

Something is eating your mouse inputs before they reach your buttons.

I see this which is worrying and potentially the rootcause of your problem:

func _unhandled_input(event: InputEvent) -> void:
	get_viewport().set_input_as_handled()

This sets all inputs as handled. What purpose does it serve? Can you just remove it and see if it works now?

Wait a second… it was working this hole time, even with that useless function I removed, If you llook closely when I use arrow keys, the grabbed button becomes slightly darker. Which means that is working. but not the indicators. because they are supposed to follow the focused button

I just have to know How I could make the indicators follow and also, even tho I press, nothing happens even tho it worked before adding get_tree().paused == true

Did you remove this function?

func _unhandled_input(event: InputEvent) -> void:
	get_viewport().set_input_as_handled()

You need to be more specific, I don’t know what you have already tried, what works, when it stopped working, did you try isolating the scene.

even with that useless function I removed

I did remove it. The menu works when the SceneTree is not paused but not when it’s paused. I really want to use get_tree.paused = true and I also have the pause menu scene’s process mode on always and the thread group is inerhited. setting get_tree().pause made the buttons inside of the Navigation node unpressable, unhoverable and unfocusable.

”If you llook closely when I use arrow keys, the grabbed button becomes slightly darker”

Even tho this is true, if it was so, then the indicator should be following the grabbed button. Maybe the issue is that if I have a node that extends a custom class, but then it also has it’s own extended script. that only the extended script could work when paused but the class itself not. If that is the case(which is likely) is it then possible to make both script’s process modes be set on ALWAYS? and no, changing the process_mode on ready() in the class won’t fix the issue

How do your nodes look like in the Remote scene tree when you have your menu open and you can’t click on the buttons? What’s the process mode? Check if the buttons are maybe disabled, or ignoring mouse input, etc.

They are also not disabled and cant be disabled because they work when the scene treee isnt paused

Your problem is likely the SubViewport. It’s eating all your inputs. Add this code to your SubViewport:

func _input(event: InputEvent) -> void:
	push_input(event)