0 votes

Hi ! I have 3 scenes (titlescreen, waitingroom & ingame) and I've been trying to smoothly load the ingame.tscn when clicking a button on the titlescreen.

Happycamper helped me with some lines of code (thx!), but I'm still not understanding how to deal with threads....

this is what I have in the titlescreen scene script :

  func _on_loadbutton_pressed():
        get_node("/root/global").goto_scene("res://scenes/ingame.tscn",true)

and in the global.gd singleton :

extends Node 

onready var thread = Thread.new()
onready var current_scene =  get_node("/root/titlescreen")

func goto_scene(new_scene,loading):

    current_scene.queue_free()
    current_scene = new_scene    
    get_tree().get_root().call_deferred("add_child",current_scene)  

    if(loading):
        thread.start(self,"prep_scene")


func prep_scene(userdata):    

    var s = ResourceLoader.load("res://scenes/waitingroom.tscn")
    var scene = s.instance()

    goto_scene(scene,false)   
    thread.call_deferred('wait to finish')

All it does at the moment is clear the titlescreen, but there's no waitingroom nor ingame scene appearing. I don't know what to put instead of 'wait to finish' in the call_deferred, and don't see where the code is checking the progression of the loading of the ingame scene...

Can someone please make it a bit clearer for me ? :/

in Engine by (245 points)
edited by

3 Answers

+1 vote
Best answer

Use ResourceLoader interactive mode and move it outside bg thread

thread.start(self,"prep_scene", ResourceLoader.load_interactive(path))

Change your "prep_scene" method like this :

func prep_scene(interactive_ldr):
 while (true):
    var err = interactive_ldr.poll();
    if(err == ERR_FILE_EOF):
        call_deferred("_on_load_level_done");
        return interactive_ldr.get_resource();

Add another func, wait thread and use loaded resource:

func _on_load_level_done():
 var level_res = thread.wait_to_finish()
 var scene = level_res.instance();
 add_child(scene);
by (46 points)
selected by

it returned an error on the resourceloader line :

parser error : identifier not found : path
 0:00:01:0639 - Condition ' _debug_parse_err_line >= 0 ' is true. returned: 0

what do you mean "move it outside background thread" ? how do I do that ? Is it why it's still not working ?

Thanks again !! --_--

"path" must be a valid path to resource, in your case this is "res://scenes/waitingroom.tscn"

i mean ResourceLoader should be run from main thread. it's already done in this line

thread.start(self,"prep_scene", ResourceLoader.load_interactive("res://scenes/waitingroom.tscn"))

hmmm I tried that but it's not working at all :
I still have the godot logo for 13 sec on launch, but when I click on "play", it goes to the waitingroom scene and stays there.

just to be sure I did things right, this is what I have now in the autoload singleton's script :

extends Node

onready var thread = Thread.new()
onready var current_scene =  get_node("/root/titlescreen")  

func goto_scene(new_scene,loading):

    current_scene.queue_free()
    current_scene = new_scene
    print("goto_scene OK")
    print("current scene =" + current_scene)
    print("new scene =" + new_scene)

    get_tree().get_root().call_deferred("add_child",current_scene)   

    if(loading):
        thread.start(self,"prep_scene", ResourceLoader.load_interactive("res://scenes/waitingroom.tscn"))       


func prep_scene(interactive_ldr):
    print("print prepscene")
    while (true):
        var err = interactive_ldr.poll();
        if(err == ERR_FILE_EOF):
            call_deferred("_on_load_level_done");
            return interactive_ldr.get_resource();

func _on_load_level_done():
    var level_res = thread.wait_to_finish()
    var scene = level_res.instance();
    add_child(scene);

Thanks for sticking with me on this problem ICatRegister, I really appreciate it !!
It feels like it's something that should be super easy to code but it's a damn puddle of mud....

the code seems to be correct, except mess with order of scenes loading.
as i understand scene "ingame.tscn" is the largest, so you need:

  1. Show title scene
  2. Preload or load waitingroom.tscn, free title scene and show waitingroom.tscn

    goto_scene(load("res://scenes/waitingroom.tscn").instance(), true);

  3. Load your largest scene in thread

    thread.start(self, "prepscene", ResourceLoader.loadinteractive("res://scenes/ingame.tscn"))

  4. Free waitingroom .tscn and show ingame.tscn

    func _on_load_level_done():
      var level_res = thread.wait_to_finish()
      var scene = level_res.instance();
      goto_scene(scene, false);
    

OK, it is working now !! :D

argh, I was starting to lose hope here : it was working but it was taking about 40 sec (!) to run the ingame scene with this thread method

BUT I was using godot v3.0.2. Just updated to 3.0.5 and now it's working perfectly fine :D (weight out of my chest !)

Thank you so much mate, you're a boss ! ;)

one laaaast question though :

get_tree().reload_current_scene()

this command, which is set in the ingame scene's script, is not working anymore.
do you know what I could replace it with ?

0 votes

Why not get_tree().change_scene("your scene") ???

by (173 points)

Doing so will block the main thread which results in not so user-friendly "freeze" if it takes more than a second to instance the loaded scene.

yes, Xrayez is right ! In my case, it takes about 13 seconds to load the ingame scene so the long freeze would look like a bug... not cool

Guys I think you can load a scene async. Also you can preload the scene

0 votes

change_scene_to allows you to use a loaded scene resource, which should be fast since the resources are already loaded.

Another way, add it to the tree and change the current_scene value.
It may not have much difference compared to change_scene_to if the scene is too big.

In that case, is the scene itself the one that will have to load parts on a thread and add one by one.

by (7,854 points)

ok, so I tried this and it's not working :

extends Node

onready var sceneToLoad = preload("res://scenes/ingame.tscn")

func _on_loadbutton_pressed():
    get_tree().change_scene_to(sceneToLoad)

It's taking long to launch the project (godot's logo screen for 13 seconds before showing the titlescreen). What I'd like is to launch the titlescreen fast, then preload the ingame scene without freezing the game so I can have a loading animation on the titlescreen while the ingame scene is being preloaded.

Did I do something wrong ? :/

Loading it in the main thread is the problem here, the answer that details about the use of threads is what you want, I guess.

Also look for "background loading" on the documentation which explains a bit how it works.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.