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. 
*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:
- Look to see if this screen has been visited before and if so, use the button the player was last on.
- Look to see if the @export variable
default_focused_control is set, and if so, use that.
- 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 
1 Like