You can use a coroutine for this but donât make _ready() a coroutine:
signal setup_done
func _ready():
setup_all_async()
func setup_all_async():
for node in get_tree().get_nodes_in_group("r_setup"):
node.setup()
await get_tree().process_frame
setup_done.emit()
The setup_done signal is optional but useful to have.
with the await get_tree().process_frame, isnât this more or less the same as if you handle the setup in _process()?
var setup_queue = []
func _ready():
setup_queue = get_tree().get_nodes_in_group("r_setup")
func _process():
if not setup_queue.isEmpty():
setup_queue.pop_back().setup()
However, I like the your coroutine variant better, as it encapsulates the problem better.
In any case, I think that both variants run on the main thread, i.e. if setup() of a single node is very expensive, it will stutter nevertheless.
An alternative could also be to use threads instead. But this really depends on the use case.
I also found this article very helpful:
also @KotKraft , why doesnât each node that needs setup, calls setup in their _ready() function?
Itâs same in how the things are executed but _process() approach is more clunky as you end up with _process() continue running after youâre done, unless you explicitly shut it down. Coroutine is a bit more elegant here.
Theread is probably the best way if there is a lot of work but special care needs to be taken because scene tree is not thread safe. The implementation details would depend on what exactly is happening in setup()