How to do "background loading" with a function instead of a resource?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By CowThing
:warning: Old Version Published before Godot 3 was released.

In a game like a roguelike, where a level if fully generated before the player starts playing it, how would I do “background loading” with the function that is generating the level? Since the level generation could be doing several loops to place and modify things across a whole level it does create a noticeable freeze in the game before the level is fully loaded.

Is there a way to have the level still generate as fast as possible and have it not freeze the game, much like background loading does for loading a resource?

I didn’t do this myself ever but the first thing I woud try is coroutines:

GDScript reference — Godot Engine (stable) documentation in English

atze | 2017-03-02 20:25

:bust_in_silhouette: Reply From: raymoo

You can try something using threads (Thread — Godot Engine (stable) documentation in English). For example:

var thread = Thread.new()

func do_stuff(arg):
    var area

    # Expensive operations here

    # Avoid loading when other stuff is happening
    call_deferred("load_area", area)

func load_area(area):
    pass # load the area

func start_load(arg1, arg2):
    if not thread.is_active(): # Can't run on active thread
        thread.start(self, "do_stuff", arg1, arg2)

More complicated solutions may involve the thread-run method emitting a signal or something like that, or a queue of areas to load in case the thread is busy.

I think this is what I want, to use threads. But I don’t really understand how threads work. I have it set up so that all the map creation is done in one function, and use thread.start() to start the function, like in your example.

I’m creating and modifying nodes in the build function. The first thing the function does is instances the base map scene, then I do all the generation, adding more nodes as children of the map, and modifying values. Then in the load function I pass in the completed map node so I can add it as a child to my main game, allowing it to enter the tree and start working.

The problem is it takes a lot longer to build in the thread. Like every time a node is created and added as a child it waits one frame, instead of doing it all as fast as possible. I’m not really sure what I’m doing wrong.

CowThing | 2017-03-03 15:39

Maybe you could try using dictionaries or arrays to create a virtual “tree” and then commit them all to scene when it’s complete? If you are using call deferred also that will cause things to wait a frame or so.

raymoo | 2017-03-03 17:04

Have you tried to change the thread’s priority? Because AFAIK it is set to low by default.

timoschwarzer | 2017-07-06 05:39

:bust_in_silhouette: Reply From: Daoist LastWish

No matter what you do, level generation cannot happen instantly. Since the player must wait until the level generation finishes, a wait is unavoidable.

What you can do instead is create a ‘loading’ scene, which shows a progress bar while you generate the level. Once the level generation finishes, you can load it & player can start playing.