godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.07k stars 69 forks source link

Add a `group_connect()` method to monitor signals from any nodes within a group #5466

Open skysphr opened 1 year ago

skysphr commented 1 year ago

Describe the project you are working on

A project with several nodes instanced at runtime.

Describe the problem or limitation you are having in your project

Often times, the following situation occurs: A controller node creates child nodes, and connects some of their signals to itself, so that it reacts to their behaviour. However some other entity might also create the same class of child nodes, which need to be connected to the controller node. Besides the duplication of connect calls, if said entity does not have a direct reference to the controller node, it needs to somehow find it, resulting in potentially hackish and unclean solutions.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

By leveraging the functionality of groups, a function such as group_connect(group, signal, target, method, binds, flags) could be created, which allows target to monitor the emission of signal by any node within group. This way, the circumstances of nodes' creation are irrelevant, as by being part of the group, they will be automatically connected to the appropriate listener.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Workflow would be almost identical to connect(), except it would be necessary to pass a reference to the node that is emitting the signal:

# Controller
var stats_bullets_ricochet: int = 0
func _ready():
    group_connect("bullets", "ricochet", self, "bullet_ricochet")
func bullet_ricochet(_bullet):
    stats_bullets_ricochet += 1

# no additional code needs to be added to nodes belonging to group "bullets", since they should not be aware of the overall stats system.

This will not allow checking for the existence of a signal when connecting, as groups may contain diverse nodes, each with its own set of signals. On a technical note, this means that the hook which implements this (most likely within scene/main/scene_tree.cpp) will have to check whether a specific signal exists or not prior to connecting it.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Workarounds are feasible, but inelegant:

Is there a reason why this should be core and not an add-on in the asset library?

It seems like a functionality that complements SceneTree's call_group() - using groups as an abstraction layer towards interacting with individual nodes.

n-gram-hub commented 1 year ago

I came here to submit the same proposal :-)

rambda commented 9 months ago

related: https://github.com/godotengine/godot-proposals/issues/766 https://github.com/godotengine/godot-proposals/issues/627

Also, according to https://github.com/godotengine/godot/pull/57541#issuecomment-1029786779

  • This use case can be addressed using the SceneTree.node_added signal, but this signal is emitted for every single node added to the scene tree - it has a huge performance cost and is only really relevant for debugging / editor tools (that performance cost is only paid once a callback is connected to the signal - but that's what would be needed here).
CyberSkull commented 6 days ago

I'd like to expand on this proposal a bit with the other signal methods.

group_disconnect(group, signal)

If we can listen to a group, we should also be able to disconnect.

group_get_connections(group)

Get all the connections in the group element.

group_is_connected(group, signal)

This is is_connected applied to the group level. Returns TRUE if a given signal is applied to the group, FALSE if not.

group_get_connected(group, signal)

This is is_connected applied to the member level. Returns a list of members of the group that are connected to the given signal, or if no signal is provided, then just list of all members that are connected to anything. This could be done by the user by iterating through all members of a group and testing with is_connected(signal), but could be much faster if instead the group keeps a list of all members with connections.

group_get_disconnected(group, signal)

The inverse of group_get_connected. This returns members of the group not connected to the signal, or without the signal argument returns all members not connected to anything. This could be done by the user by iterating through all members of a group and testing with is_connected(signal), but could be much faster if instead the group keeps a list of all members without connections.