When should I use signals vs node groups?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Peter O’Malley
:warning: Old Version Published before Godot 3 was released.

GDScript has both signals and node groups, which seem to serve similar purposes (at least since the example used in the tutorial linked above is “notify all guards when the player is discovered”, which seems “signal-like” to me). I’m new to Godot and trying to figure out the idiomatic (or just simpler) way to do things.

When should I use one vs the other?

It seems that signals are more fine-grained; the same signal emitted from different senders (e.g. “pressed” from various buttons) are treated independently. The downside to this is that connecting the signals together is more involved (you have to have a reference to the emitting object).

By contrast, using node groups to call a function on all the members seems more appropriate for global changes in state. The receiving nodes don’t need to have a reference to anything (other than the name of the group to join) in order to register themselves.

Does this make sense? Any other principles to keep in mind?

:bust_in_silhouette: Reply From: Zylann

You already described most of their characteristics.

I most likely use signals in GUI elements, or re-usable scenes, because there are an elegant way of sending events to whatever node is using them. Want a level selector? Fine, instantiate it where you want and listen to level_selected, done. Want two level selectors? Same, it just works. And most importantly, they are scoped and named with signal my_signal, so you are less likely to do a mistake.

I used groups when I was too lazy to find a design where a node has to communicate an event to a node which is far away in the hierarchy. I think the use case where multiple nodes listen to the same event is the best, because you simply don’t have to come up with boilerplate code to dispatch the event.

But… in general I try to avoid groups whenever I can because they have no scope (global), their name is global (another problem for addons), and when you open a project, nothing tells you which groups are in use and which functions are called on them unless you read all the script files and scenes.
You could say “this is my project, I know everything”, but come back 1 month later and you’ll be like “did I made a group for that thing?”.
In small projects they can still be of use, because it’s less tedious to listen for global messages. Maybe they are going to be improved in the future, but at least I try to have a .gd file listing all groups to have auto-completion and self-documentation.

Thanks! I’m with you on the hesitancy to use things in the global scope. I, too, put the node group names in my constants.gd (i.e. an autoload script) to help avoid typos and so there’s a bit of documentation.

I find the signal naming a bit clunky, though–you still need to know the name of the signal when you connect to or emit it. What I mean is: you declare a signal with signal my_signal, but then you emit with emit("my_signal", ...) and you connect to it with object.connect("my_signal", ...), so you need to put "my_signal" as a string. So I put that in my constants.gd as well, which seems very un-DRY. It would be nice if my_signal became a member of the object in which you declared it, i.e. so you could say self.emit(self.my_signal, ...) or object.connect(object.my_signal, ...). That would at least fix one problem.

I also find that I need to search through all my script files to figure out where I’ve got a signal connected to, even though it’s usually very easy to find where it’s emitted. If the GDscript editor had a “find usages” feature, together with my earlier suggestion to make the signal a member its class, that would be easier. (Or if there were a global search function, I guess.)

Peter O’Malley | 2016-08-06 13:09

You don’t need to put signal names in constants, because Godot gives you auto-completion already, even if you specify them as a string. Again, that’s because signals are declared, and the editor recognizes where you use them.
It also makes no sense to declare them in constants.gd because signals are not global things.
But I agree passing them as string is a bit weird, just like funcref("func_name_as_string").

Most of the time, I prefer to connect signals with code, but when I do from the editor, I ensure that it gets a specific naming convention, as proposed by the generator: _on_Stuff_this_happened(), so handler functions are easy to spot.

Zylann | 2016-08-06 13:18

I didn’t know the editor already gave you auto-completion for signals. TBH I’m still figuring out just where you get auto-completion and where you don’t–I thought I decided that you don’t get it for signals but I must not have been thorough enough. The main reason I was putting them as constants was to avoid typos, but I agree it was very fishy, and that should be reduced with auto-completion. (Though renaming signals would be tricky, then.)

I have found, like you, that I greatly prefer not to use the editor for signal connections (or node groups), because I find it easy to lose track of things that way. But that could be because I come from a pure coding background and organize my thoughts that way.

Thanks again!

Peter O’Malley | 2016-08-06 16:05