It is generally advised in the Godot community to use signals as much as possible.
I agree, it’s a great way to reduce coupling between components, which allows for a more pleasant experience when testing and implementing new features.
However, from my experience, I’ve found that it is not always clear when it’s appropriate to use signals. But I also found a few situations when it’s 100% appropriate to use them. Here’s one of them:
Premise
Imagine you are creating a game where the player can mine blocks. It’s easy to imagine a script like this for this game:
class_name Player
func mine() -> void:
# ...
Now, say you want to keep track of how many blocks the player has mined. The first impulse for any practical person is this:
class_name Player
var _blocks_mined := 0
func mine() -> void:
# ...
_blocks_mined += 1
At first, it’s perfectly alright, but as you increase the number of stats you want to keep track of, your player script may become unruly. There will be a lot of variables, and variables will be manipulated in seemingly random places.
Solution
Clearly, it is better to do something like this:
class_name Player
signal block_mined
func mine() -> void:
# ...
block_mined.emit()
Or this:
class_name Global
signal player_block_mined
class_name Player
func mine() -> void:
# ...
Global.player_block_mined.emit()
class_name ResourceManager
var _blocks_mined := 0
func _ready() -> void:
Global.player_block_mined.connect(_on_player_block_mined)
func _on_player_block_mined() -> void:
_blocks_mined += 1
General rule
I think this approach works in general and can be applied as simple rule to follow when developing:
Every time you need to add a new variable to a script, take a second and think if it’s possible to extract it into a different one and manipulate using signals.
As an added benefit, this approach also lets your imagination run and helps generate new ideas. Personally, every time I introduce new signal to a script, it usually makes me wonder of all the other things that could react to it.