Best architecture for object collision and signal emission

Godot Version

4.3.stable

Question

I am trying to figure out the best architecture when using signals for collision.

There are two ways I can think of handling collisions:

  1. Game Objects detect the player and emit a signal
  2. Player detects everything and emits signals

Assume that the Player has an area2d for detecting objects. Assume the objects all have area2d.

Option 1 makes logical sense because each object would be responsible for sending out its own signal. This keeps everything nicely organized. The problem is that every new object that gets made needs to have its signal connected (to the level or to a GameManager script). So if I want to add 100 coins and 25 spikes, I’d have to manually connect 125 signals. This is tedious and prone to error if I forget to connect a signal or accidentally disconnect one.

Option 2 has the player handle all collisions so I only have to connect the player’s area_entered signal one time and it handles everything. The downside of this method is that as more types of objects are added to the game, the bigger the player’s on_area_entered function gets. It also makes less logical and organizational sense to me.

Example:

func _on_area_entered(area: Area2D):
    if (area.is_in_group("coin")):
        coin_collected.emit()
    if (area.is_in_group("diamond")):
        diamond_collected.emit()
    if (area.is_in_group("spike")):
        spike_hit.emit()
    if (area.is_in_group("acid")):
        acid_hit.emit()
    ...

I’m wondering if there is some other model or architecture that I’m not aware of that I should be using instead of either of these methods. If not, I’d like to know which of these methods is considered superior.

Could you emit what group was picked up with the signal

signal picked_item(item_group: String)

func _on_area_entered(area: Area2D) -> void:
    for group in ["coin", "diamond", "spike", "acid"]:
        if (area.is_in_group(group)):
            picked_item.emit(group)

That’s interesting. I hadn’t considered that. Thank you!

Is that how you do it in your games? I’m curious how larger games handle this.

If I have a lot of items then I keep what item each area is on the area and detect collisions on the player, pulling that information from the collision. Like your is_in_group solution but I personally do not use groups very often. I would prefer a base script like a generic item.gd with this information of what the item is and what it does, or if I cannot attach a script I would try meta data through get_meta.

1 Like

Interesting. So instead of is_in_group, you’re using a variable in your script? Something like a object_type variable?

The closest thing in my own games emits the entire object

signal picked_item(item_object: Item)

func _on_area_entered(area: Area2D) -> void:
    if area is Item:
        picked_item.emit(area)