Best way to for a node to talk to a UI that's "far away"?

Godot Version

v4.3.stable.mono.official [77dcf97d8]

Question

I’ve found myself in the following situation
my_head_hurts
We have an Ability Manager with 0-4 Ability scenes each with a Timer that wants to connect to a Button in the UI to show duration left (red arrow), the problem is that the Timer and the Button are so far away.
I could transmit signals up to the Ability Manager, then sideways to its UI sibling, then call down to the Ability Button, but is that really the best way to go about it?
I’ve also read up on Event Bus patterns, but I’m afraid that could lead to too many Events to handle.

If you want to continue displaying the remaining time, you should directly modify the Button
eg:

var no_time:float=0
func _process(delta: float) -> void:
	no_time=no_time-delta
	## decimals
	text=str(no_time,"s")
	## int
	text=str(int(no_time),"s")

The best way for nodes to communicate with “distant” UI is to use @export
What you are using .Net, But .Net also has its own @export

I’m already trying to modify the button, the problem is in how the button can know the time_left property of the Duration Timer, giving the button its own duration to keep track of seems counter-intuitive.
In this case connecting them with @export is not possible either since an ability doesn’t exist before runtime.

Hi (sorry for my English)
I’m a newbie but I think the best thing to do would be the following.
I’m putting a kind of pseudocode.
An autoload with a reference to a “ButtonsManager” script (or the UI script) that has access to the buttons.

# Autoload:
class_name Global

var buttons_manager:ButtonsManager

# --------------------------
# ButtonsManager:

class_name ButtonsManager

@export button1: ...

func _ready():
	Global.buttonsManager = self

func update_button(id_button, time)
	...
	if id_button == "button1":
		button1.set_time(time)
	...

# -----------------------------------
# In the DurationTimer you can do:
	Global.buttonsManager.update("button1", time)

Maybe this will help Node caching or use groups to access nodes without knowing their path. If i correctly understand the issue.

It does look like a classic model/view/controller situation, doesn’t it? If you have a place where you instantiate the view (button) for the model (Ability), you can connect the button to some “model_changed” signal emitted from the Ability, whenever the timer updates.

If you want the UI and the Ability completely decoupled, I would go for the event bus. Is it really going to be that many signals? One per second per Ability?

If you have a place where you instantiate the view (button) for the model (Ability), you can connect the button to some “model_changed” signal emitted from the Ability, whenever the timer updates.

Where would that happen? The Owner feels like the only place that could happen since the UI (that instantiates the button) doesn’t know about the instantiated abilities in the Ability Manager. Current solution that I implemented was to just transmit up to the Ability Manager, then sideways to UI, then call down to the button.

Is it really going to be that many signals? One per second per Ability?

Every process tick currently, the duration is displayed with a progress bar and a label. I did do the same thing with a global EventBus, which just held the relevant signals, and connected there between Ability and in the Ability Button, and that worked great. But with my setup where multiple of the same ability can exist in different places, that caused collisions.

@capitanlopez It’s nice but won’t work for me, buttons and ability nodes don’t exist before runtime, so they can’t be assigned with an @export. Could of course be solved by just having a list of something and assign ID’s in there, but at some point it just looks like an EventBus.

@SirG I use unique names in a bunch of places, but as mentioned before, nodes don’t exist before runtime so not much use there. Using groups wasn’t something I thought of before though, I think it’ll be possible since each ability is its own scene and so is the button.

(post deleted by author)

In my opinion, the Event Bus pattern very appropiate because of how simple it is to build, use and mantain. I use it in all my projects because it is also very flexible.

Also, you don’t need to emit every signal through the Event Bus, therefore it should be easy to avoid having “too many events”.

I don’t really see the disadvantages of this pattern for these scenarios, so if anyone has any opinion on this, it would be good to know.

Mentioned it, but I tried an EventBus and it worked great.
There was a slight complication in my case though. Each ability has a StringName IDs to identify themselves (like &"cool_ability"), buttons connected to abilities use this same ID.
So if multiple seperate instances of the same ability exists, when one of them emits their duration on the EventBus, all buttons with the same ID displays duration, even if the button hasn’t been “activated”.

But that’s just a problem with me not figuring out how to set up a proper UUID system for this, not related to the EventBus.