As the title explains, I am curious what the easiest way to check if a property, or more than one property(s), are all true or all false from all of a parent node’s children in GDScript;
this i would say would of course use a for loop, however I have never truly used them, at all, and I am simply curious how one would go about doing this.
Take a step back and tell us what you are trying to accomplish. Because I think there’s an easier way.
But to do what you want, you want a Recursive Function. It looks like this:
First you need the same value to exist on EVERY NODE. Otherwise you’ll need code to filter out node types you don’t care about.
var is_flag_set: bool = true
Then, if you wanted to check to see if any node had the value set to false and fail if it did, you’d do something like this.
func _is_flag_set(node: Node) -> bool:
if not is_flag_set:
return false
var return_value: bool = true
for subnode in node.get_children():
if not _is_flag_set(subnode):
return false
return true
Thank you very much also, this is extremely helpful:
Ahh yes,
now, backstory, i have been messing with my UI, at this point, it is slow, unconventional, and overall just awful and annoying;
my main goal here was that, i could use get_viewport().gui_get_focus_owner() alongside a flag in each button, which would in this case check if itself is being focused,
and the parent object which would be a grid container, would simply check if all child nodes for the flag;
if none of them are true, then it should focus child 0. This could be controlled by a menu level
global variable and exported index variables that determine when it should focus or not to avoid conflicting behavior.
Another thing was, I did not see anyone else asking about this, and I assumed that me asking would do others in the future a help.
Yess, if any of the child nodes lacks the flag, it will throw an invalid reference error,
Anyways,
at this point I do not think there is much of an easier way, but at this point i seem to like “re-inventing the wheel with cubes” very often, and indeed I lack 2 or more years of experience.
Like I said, there’s an easier way. Just track it. Every Control has a focus_entered signal. Just connect to the ones you care about and store that information somewhere. I do it at the screen level in my User Interface Plugin.
## 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)
When you open or close a screen, it remembers where the user last was and focuses on that Control. It also defaults to focusing on the first Control in the the list unless you set a specific Control to be used as the default.
How do you make sure there aren’t any conflictions?
before this topic, I did a very, very simple _focuser() function,
func _focuser():
if Snes.menu_level == index1 or Snes.menu_level == index2:
for grid_buttons in self.get_children():
if get_viewport().gui_get_focus_owner() != grid_buttons:
self.get_child(0).grab_focus()
I would assume it is very archaic, but considering i want a retro feel, that isnt much of an issue,
either way, if you dont mind me asking,
what are the pros/cons of my initial method, versus the easier method, assuming that i dont want option memory,
apologies if it comes across as rude, I am curious about each method
I mean if there were multiple instances of the same node and script, this may be irrelevant though.
the _focuser() mostly handles itself, but there is 1 other level involved other than the actual global variable, but this is only so that options can change the level independently,
to me it seems like less syntax, i highly doubt that to be the case though.
at this point i should probably stop completely redoing my systems, but either way, thank you for your help.
i mean multiple copies attempting to focus at the same time, not exactly copies being the issue, but the action of calling focus on more than one node simutaniously.