godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 93 forks source link

Add a mechanism to GDScript to wait for signal's completion #2518

Open jay20162016 opened 3 years ago

jay20162016 commented 3 years ago

Describe the project you are working on

I am working on a game, with a event system. I want all events to finish before returning to the function raising the event.

Describe the problem or limitation you are having in your project

The approach mentioned in godotengine/godot#3235, while it works for calling a single function and yielding it works for single functions. However, it doesn't give a clean way of waiting for a signal's completion where multiple functions are connected to the signal, as in this case. Is there a clean way of waiting for a signal's completion in this case?

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

Add a way to make a signal blocking, or a way to wait for a signal's completion

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

# a.gd
emit_signal('something')
wait_for_signal_completion('something')
print("hello world")
# b.gd
connect('something', 'func_x')

func_x:
sleep(10)

would raise 'something', wait for 10 seconds, then print hello world.

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

Probably not

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

It concerns new functionality on signals that could not easily be implemented as a add-on

Calinou commented 3 years ago

Have you checked await in the new GDScript coming to Godot 4.0?

jay20162016 commented 3 years ago

It doesn't seem to handle this particular use case.

YuriSizov commented 3 years ago

I think signals are already blocking, but handlers that you connect to them may not be. And I don't see how a signal could await for a connected method to finish, and even why that would be a good idea and not an antipattern.

vnen commented 3 years ago

I think signals block too, have you tested this code? AFAIK unless it's connect as deferred it should still block when you emit a signal while calling all the connected functions.

dalexeev commented 3 years ago

I am working on a game, with a event system. I want all events to finish before returning to the function raising the event.

enum {
    EVENT1,
    EVENT2,
    EVENT3,
    EVENT_MAX
}

signal event_finished(event_id)
signal all_events_finished()

var _event_mask = 0

func finish_event(event_id):
    if event_id < 0 or event_id >= EVENT_MAX:
        assert(false, "Invalid event_id!")
        return

    if _event_mask & (1 << event_id):
        assert(false, "The event has already been completed!")
        return

    _event_mask |= 1 << event_id
    emit_signal("event_finished", event_id)

    if _event_mask == (1 << EVENT_MAX) - 1:
        emit_signal("all_events_finished")