Game Update, Menu focus issue

Hello everyone, this post was initially supposed to be for another issue, but it turned out i realized it was because i was firing the same function at the framespeed lol

Anyways, i am not entirely sure why, and i have tried several godot-built in functions to help,
but whenever i open my menu, it doesnt allow controller, uh, controls unless i tap the menu with my mouse cursor first, not a fatal error, but its annoying.

Fixes?

And for the showcase, i recorded a bit of me messing with the menu, the mountains could use an upgrade, as well as many other things can be improved on, but still, i like what i have so far.

(soundtrack due for editing, currently better than silence tho, made with real BRR instruments!)
I am glad i restarted my project, because i am doing a lot better knowing what i am doing.
Any feedback is appreciated. Have good day

(As for the type of post, i wish there was a help + showcase, and i dont think this fits in general…
currently using Dialog Manager, love it so far )

You may need to use grab_focus() on a control node when opening each menu.

2 Likes

Perfect, thank you very much, implementing now. :slightly_smiling_face:

*OK, i tried it, the console gave a warning error advising to use
self.set_focus_behavior_recursive(Control.FOCUS_BEHAVIOR_ENABLED)
self.set_focus_mode(Control.FOCUS_ALL)
but this stops the controller from being able to focus at all . . .
hmm. . .

Which node did you grab_focus on? When opening the menu you may need to pick a good start %FirstButton.grab_focus(), not just any Control node

2 Likes

Take a look at my User Interface Plugin. Specifically for the Screen node. Most of the code is for connecting the buttons and setting focus. I take a three tiered approach for setting focus on each screen:

  1. Look to see if this screen has been visited before and if so, use the button the player was last on.
  2. Look to see if the @export variable default_focused_control is set, and if so, use that.
  3. Scan all the nodes on the screen and assign the first button I find as the focus.
## A default screen that is tracked by the UI autoload. All buttons in the screen are automatically
## hooked up to play the click sound set up in the Sound autoload. It also allows you to set a
## default control for when the screen loads, and tracks the last button pressed for when a player
## returns to this screen.
@icon("res://addons/dragonforge_user_interface/assets/textures/icons/screen.svg")
class_name Screen extends Control

## The control that receives focus by default when starting.
@export var default_focused_control: Control

# For tracking the last focused button when traversing menus.
var _button_last_focused: BaseButton
# The button to use if no default button is set.
var _default_button_focus_fall_back: BaseButton


func _ready() -> void:
	hide()
	visibility_changed.connect(_on_visibility_changed)
	child_exiting_tree.connect(_on_control_removed)
	child_entered_tree.connect(_on_control_added)
	_connect_buttons(self)
	UI.register_screen(self)


func _on_visibility_changed() -> void:
	if visible:
		_set_focus()


func _on_control_added(node: Node) -> void:
	_connect_buttons(node)

func _on_control_removed(node: Node) -> void:
	_disconnect_buttons(node)


# Sets focus for a control for keyboard and gamepad users. Picks the last
# button that had focus, then the default if set, then defaults to the first
# button it finds on the screen.
func _set_focus() -> void:
	if _button_last_focused:
		_button_last_focused.grab_focus()
	elif default_focused_control:
		default_focused_control.grab_focus()
	elif _default_button_focus_fall_back:
		_default_button_focus_fall_back.grab_focus()


# Stores the currently selected button for focusing upon exiting and re-entering
# the screen. Only buttons are tracked, since to enter or exit a screen, you
# must typically be on a button.
func _on_button_focused(button: BaseButton) -> void:
	_button_last_focused = button


# Play the default button pressed sound stored in [Sound] (if [Sound] exists).
func _on_button_pressed() -> void:
	if get_tree().root.has_node("Sound"):
		var sound: Variant = get_tree().root.get_node("Sound")
		sound.play_ui_sound(sound.get_sound("button_pressed"))


# Connects any button in the passed node for the button click sound and for
# default focus. Does the same for any buttons farther down the tree.
func _connect_buttons(node: Node) -> void:
	for subnode in node.get_children():
		if subnode is BaseButton:
			if not _default_button_focus_fall_back:
				_default_button_focus_fall_back = subnode
			subnode.pressed.connect(_on_button_pressed)
			subnode.focus_entered.connect(_on_button_focused.bind(subnode))
		_connect_buttons(subnode)


# Disconnects any button the passed node for the button click sound and for
# default focus. Does the same for any buttons farther down the tree.
func _disconnect_buttons(node: Node) -> void:
	for subnode in node.get_children():
		if subnode is BaseButton:
			if _default_button_focus_fall_back == subnode:
				_default_button_focus_fall_back = null
			subnode.pressed.disconnect(_on_button_pressed)
			subnode.focus_entered.disconnect(_on_button_focused.bind(subnode))
		_disconnect_buttons(subnode)

1 Like

Aha!!! Thank you very much! Tried it, works perfectly (took a lot of navigation to work out the kinks, but menu focus, visibility, etc. works perfectly now, truly i am very grateful),

I really like your plugin’s structure, thank you very much for sharing your strategy!
I am very thankful that there are many helpful people on this forum, i hope i can make it up to you all eventually :slightly_smiling_face:

1 Like