Loops without freezing the whole game

Godot Version

4.5.1.stable

Question

How can i make a loop that wont freeze the whole project while running?

like the game runs while the loop is also running in the background

example:

func _ready() -> void:
    for i in 150:
        await get_tree().create_timer(0.1).timeout
        print(i)

or

func send_position_setup():
   for i in get_tree().get_nodes_in_group("r_position")
       await get_tree().create_timer(0.1).timeout
       i.second_setup()

note: no, i wont use _process() or timers since they don`t do exactly what i want

But that’s exactly how you should do it. What’s your problem with these?

What is it that you exactly want?

2 Likes

What do you want?

1 Like

also not to pile on, but you say you won’t use timers but you literally put a timer in your example code

3 Likes

i meant “timer” as a node

for it to be clean and not to always run the background

Just use a function that allows the timer to set off again each timeout until an int increments up to whatever number you want it to reach.

Something like

Var loops: int =0

Func on_timer_timeout():

Loops += 1

If loops >= 150:

Timer.stop()

2 Likes

Yeah but what is “it”?

2 Likes

By “it“ i meant the loop function.

Yes but what is this function supposed to do? What are you actually making? Describe your exact use case.

2 Likes

Instead of setting up all objects in one frame making it freeze, it spreads out by slowing the game just a bit using delays.

Here`s the original code:

func _on_setup_finish_timeout() -> void:
	for i in get_tree().get_nodes_in_group("r_setup"):
		i.setup()

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?

2 Likes

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()

1 Like