Reputation: 2804
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
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