DanielRead
DanielRead

Reputation: 2804

Godot listening to signals from multiple instances of the same scene

I have the following Scenes: Player Enemy Attack

When an Attack collides with the Enemy, the Enemy emits a "onHit" signal.

The Player listens for that signal and bounces back.

This is all working good, but now if I duplicate the enemy so there are multiple Enemy scenes, how do I listen to the signal on all of them? Is there a way to grab all instances of a Scene and connect to all of their signals? Is there a better way of sending this message back to the Player?

I'm very new to Godot so any advice is very helpful! Thanks.

Upvotes: 13

Views: 11358

Answers (1)

Theraot
Theraot

Reputation: 40295

Use a Signal Bus.

Yes, you could iterate over the nodes and find all the enemies (e.g. by comparing their script). However, it is easier if all the enemies register themselves to a list (or a group) on _ready. However, you don't need any of that.

The insight is this: An object can emit signals of other objects.

We take advantage of that by creating a Signal Bus. Which is a a common pattern in Godot. It goes as follows:

  • Create an autoload (singleton) script. Let us call it SignalBus.

  • In the script, define signals. And nothing else. *In our case, we define on_hit:

    signal on_hit
    
  • Every place that needs to emit a signal does it from the signal bus. In this case the enemies do this:

    Godot 3 or Godot 4

    SignalBus.emit_signal("on_hit")
    

    Or using the new Godot 4 syntax:

    SignalBus.on_hit.emit()
    
  • And where you need to handle the signal, connect to it. For example on _ready. Something like this:

    Godot 3

    func _ready() -> void:
        SignalBus.connect("on_hit", self, "_on_hit")
    
    func _on_hit() -> void:
        # whatever
        pass
    

    Godot 4 legacy syntax (this syntax eases upgrading code)

    func _ready() -> void:
        SignalBus.connect("on_hit", Callable(self, "_on_hit"))
    
    func _on_hit() -> void:
        # whatever
        pass
    

    In Godot 4 we can simply pass a method reference as a Callable:

    func _ready() -> void:
        SignalBus.connect("on_hit", _on_hit)
    
    func _on_hit() -> void:
        # whatever
        pass
    

    Or using the new Godot 4 syntax:

    func _ready() -> void:
        SignalBus.on_hit.connect(_on_hit)
    
    func _on_hit() -> void:
        # whatever
        pass
    

This way the emitters and receivers of a signal don't need to know each other. They only need to know the Signal Bus. Which is available everywhere (by virtue of being an autoload).

Upvotes: 37

Related Questions