I've been making great progress with my first project with thanks to people on here and am starting to bring everything together.
Part of that is creating a GUI which I am feeding information with signals.

However, I was under the impression that the benefit of signals was that it allows your nodes to remain independent and that is a good thing. What I don't understand though is that I still need to connect the signals:

	hero = get_parent().get_node("Hero")
	hero.connect("update_gui", update)

So how is this any better than just calling the function directly? Am I making a mistake?

  • xyz replied to this.
    • Edited

    Tomkun It's exactly like calling a function. In fact it is a function call. The benefit is that you can make many connection to a signal and they all get called automatically at once when the signal is emitted. You also don't need to check if a signal receiving object exists or not. Engine does all the cleanup for you so there's much less possibility of "invalid reference" runtime errors than with "manual" calls.

      xyz
      That makes sense; I was hoping though that it would allow me to test my GUI without loading up the whole program. Unfortunately it fails when it can't find and connect the Hero node.
      Is there a way around that using signals?

      • xyz replied to this.

        My approach is to declare the signals at a global level.

        Then any node can connect to a signal as a listener, and any node can emit a signal. The emitting node and the listening node don't have to know about each other. They only have to know the name of the autoload, the name of the signal and the signal parameter types (if any).

        Example (Godot 4):

        # signal declaration, in a script that's attached to an autoload named Events
        extends Node
        
        @warning_ignore("unused_signal")
        signal show_credits(visible: bool)
        # in the signal listener node
        Events.show_credits.connect(_on_show_credits)
        ...
        func _on_show_credits(visible: bool) -> void:
            ...
        }
        # in the signal emitter node
        Events.show_credits.emit(true)

        (Warning: xyz may whine about this, because he hates autoloads.)

          DaveTheCoder
          Well, I am also usually adverse to using Globals myself if possible, but I think this is the simplest solution for my needs.

          I can imagine signals are going to get very complicated if I have a lot of them being called from different places. The switchboard approach seems good. Thank you.

          There isn't a way to bulk ignore unused_signal errors, is there?

            Tomkun Well, I am also usually adverse to using Globals myself if possible, but I think this is the simplest solution for my needs.

            I agree on both counts.

            Tomkun There isn't a way to bulk ignore unused_signal errors, is there?

            You can do that in:
            Project / Project Settings... / General / Debug / GDScript

            I usually prefer not to do that, since that might suppress a warning that indicates an actual problem with the code.

              • Edited

              Tomkun Always call connect() from higher in the hierarchy to below. Child nodes have no business calling anything in parent nodes, including connect(), or even expecting there is a parent in existence. Design your scene architecture and code hierarchy on that premise. There's this rule of thumb in Godot: call down, signal up.

                xyz call down, signal up

                That reminds me:

                DaveTheCoder The emitting node and the listening node don't have to know about each other.

                There is one caveat in my approach. The listener has to be connected when the signal is emitted. The parent-child hierarchy can be relevant in that respect.

                Here's a way of using a lambda to defer a signal emit until the scene tree is fully built:

                (func(): Events.show_credits.emit(true)).call_deferred()

                xyz
                That's interesting. How about siblings?

                My hierarchy is like this at the moment:

                Game
                |->Level
                |->Hero
                |->GUI

                When the hero picks up a key for example, I want to send a signal (or call) to the GUI to update itself. I hope that makes sense?

                • xyz replied to this.
                  • Edited

                  Tomkun Inter-sibling signaling is fine. The important thing is to do all connect() calls from Game. That way you can run each of them individually for testing because no connections will be made if they're not "in" the Game.

                    xyz
                    Ahh, now I get it!
                    That makes a lot of sense now, thanks!

                    Just for confirmation:

                    Previously in my GUI script I was doing this:

                    	hero = get_parent().get_node("Hero")
                    	hero.connect("update_gui", update)

                    But how should this be done from the parent?

                    hero.connect("update_gui", gui.update)
                    or perhaps
                    gui.hero.connect("update_gui", update)

                    Never mind, it was the first one!

                      Tomkun hero.connect("update_gui", gui.update)

                      In Godot 4, this is preferable:
                      hero.update_gui.connect(gui.update)

                        DaveTheCoder yeah reading this thread i was confused, because i always used this. but then again i only worked with 4.0 and up.

                        DaveTheCoder @warning_ignore("unused_signal")

                        this is good, because sometimes you have signals that are passed as strings and then godot will complain that it is not connected but it actually is. I already posted an issue on gh but devs brushed me off..