Search code examples
signalsgodotgdscript

Godot Signal "Emitted" but not "Received"


I've been trying to send a signal from a singleton script in this manner:

if(Input.is_action_just_pressed("ui_select")):
    emit_signal("update_inventory")
    #More Code underneath - 

I have declared the signal on a UI Element that displays my inventory items. Like this -

signal update_inventory

And the declaration of a function that is connected to it via the editor -

func _on_Inventory_update_inventory():
    #My code here

I tried to understand a similar problem (I think so?) here. I couldn't understand it completely. I saw that someone said in it - Signals are local to an object. Are they, really? If yes, why not call the function normally? The point of signals would get defeated, wouldn't it?

I need a way to solve the signal problem as well. Maybe I'm not configuring it properly. Where am I going wrong?

I'm more than happy to provide any more information if required.


Solution

  • Signals are local to an object. Are they, really?

    Yes. If you define a signal in node A, that signal must be emitted from node A. Typically you'd call emit_signal from A itself, as a signal is a way for one Node to notify others that something interesting has happened.

    If for some reason you do want to emit the signal from another Node, you need to get a reference to the Node containing the signal somehow:

    $Path/To/A.emit_signal("something_happened")
    

    This is a good thing, as if signals were always global, they wouldn't be nearly as useful. Consider the Button.pressed signal. In a scene with many buttons, we have to be specific about which button was pressed. Was it the "Start game" button, or the "Quit Game" button? The ability to just call emit_signal("pressed") from any node in this scene would be ambiguous.

    However, some signals might be "global" in nature, like game_over. For this, you can use an "Event Bus". It sounds like you might be trying to do this, but you should define your singals inside the singleton, like so:

    # Events.gd (singleton)
    signal game_started
    signal game_over
    

    Then individual nodes can choose to listen for or emit these signals from anywhere in the tree. For example:

    Events.emit_signal("game_started")
    
    # in another node:
    Events.connect("game_started", self, "start_music")
    

    This is often referred to as a publish-subscribe pattern.

    why not call the function normally?

    Calling a function normally requires that the "sending" Node has specific knowledge of the "receiving" Node, which increases the coupling between nodes. Tighter coupling means less flexibility, which means slower game development!

    Consider our UI example. If buttons were responsible for directly calling an action when clicked, every single button would need custom code to invoke a specific action. We'd have start_button.gd, quit_button.gd, and so on.

    By using signals, we just need a single Button script (the one built in to Godot, actually!). This button is just responsible for emitting a signal when clicked, which we can then wire up to any arbitrary action.

    When thinking with signals, you can define small, self-contained nodes with no knowledge of the outside world. Such nodes can be dropped anywhere in your scene tree and wired up as needed, allowing you to build out scenes quickly!

    I need a way to solve the signal problem as well

    I hope after reading this you'll see that signals aren't a problem to be solved. Coupling is the problem, and signals are a tool to solve it.