+2 votes

Hi,

Is there a better way to handle multiple scenes?

Currently using this:

export(String, FILE, "*.tscn") var world_scene

func _physics_process(delta):
    var bodies = get_overlapping_bodies()
    for body in bodies:
        if body.name == "Player":
                get_tree().change_scene(world_scene)
in Engine by (93 points)

Hi folks,

I just noticed ChangeScenesHelper on the AssitLib in Godot.

https://github.com/PedroSTVeras/AssetChangeScenes

Too bad the asset has a 404 error during download.
At least the Github version is there.

3 Answers

+2 votes
Best answer

Yes, there is.
I'm currently working on a singleton class "LoadManager" which will load the scene/scenes needed in one main scene using an InteractiveLoader that works in parallel with a loading scene i'll release it soon as a Platformer Game Starter Kit in the AssetLib

With this class, there's a function like changesceneto but called goto_scene
it has some more arguments
and it works like this

gotoscene("res::pathToScene", string array scenes to destroy, bool destroy all scenes)

there are also basic functions like addScene(string path)
and freeScene(string path) which only works if the scene is found

The last 2 you can use for your multiple scenes, like, if you need an overlay panel like a Pause menu you just do

onready var loadmanager = get_tree().get_root().get_node("Loadmanager")

loadmanager.add_scene("path to scene")

Here's the loadmanager singleton script, it may help you but you will need to tweak it for your needs because i can't post here also the loading screen scene

at least it's a good example for the Loader and InteractiveLoader classes

extends Node

var root
var mainscn
var scenes
var loader
var loadscreen
var animated_val
var processtimer
var label
var tween
var progressbar
onready var time_max = 1000 #msec

func _ready():
    root = get_tree().get_root()
    mainscn = root.get_node("MainScn")
    set_process_internal(true)
    set_process(false)

func show_error(err : String) -> void:
    var errDiag = root.get_node("errDiag")
    if(errDiag == null):
        errDiag = ConfirmationDialog.new()
        errDiag.set_name("errDiag")
        errDiag.set_title("Runtime Error!")
        root.add_child(errDiag)

    errDiag.set_text("Error Code: " + err)
    errDiag.popup_centered(errDiag.get_minimum_size())

func getActiveScenes() -> Array:
    scenes = mainscn.get_children()
    return scenes

func free_scene(name : String):
    return call_deferred("def_free_scene", name)

func goto_scene(path : String, scenes_to_destroy, freeAll : bool): #remvoe all other scenes
    return call_deferred("def_goto_scene", path, scenes_to_destroy, freeAll)

func _add_scene(path : String):
    var tmp = ResourceLoader.exists(path)
    var scene
    if( tmp ):
        var rl = ResourceLoader.load(path)
        scene = rl.instance()
        mainscn.add_child(scene)
    else:
        show_error("ResourceLoader.exists(" + path as String + ")" + " returned: " + tmp as String)
        return false
    return scene

func def_free_scene(name : String) -> bool:
    getActiveScenes()
    var count = scenes.size()
    var found = false

    while (count != 0):
        if(scenes[count].get_name() == name):
            found = true
            break;
        count -= 1
    if(!found):
        show_error("def_free_scene(name : String), could not find the scene (" + name + ") to free")
        return false

    scenes[count].queue_free()
    return true

func def_goto_scene(path : String, scenes_to_destroy, freeAll : bool) -> void:

    #FREE SCENES
    if (!freeAll and scenes_to_destroy != null):
        var count = scenes_to_destroy.size()
        while (count != 0):
            scenes_to_destroy[count-1].queue_free()
            count -=1
    elif (freeAll):
        var count = getActiveScenes().size()
        while (count != 0):
            scenes[count-1].queue_free()
            count -=1

    loader = ResourceLoader.load_interactive(path)
    if loader == null: # check for errors
        show_error("ResourceLoader.load_interactive(" + path as String + ")")
        return

    loadscreen = ResourceLoader.load("res://LoadingScn.tscn")
    loadscreen = loadscreen.instance()
    mainscn.add_child(loadscreen)

    label = root.get_node("MainScn/LoadingScn/loadbg/CenterContainer/VBoxContainer/LoadLabel")
    tween = root.get_node("MainScn/LoadingScn/loadbg/CenterContainer/VBoxContainer/tween")
    progressbar = root.get_node("MainScn/LoadingScn/loadbg/CenterContainer/VBoxContainer/progressbar")
    processtimer = root.get_node("MainScn/LoadingScn/Timer")
    processtimer.connect("timeout", self, "_processing")
    processtimer.start()
    #set_process(true)

func _processing() -> void:
    var err
    var resource

    if(loader == null):
        show_error("while pooling resource")
        return

    #while(OS.get_ticks_msec() < (t + time_max)):
    err = loader.poll()
    if(err == ERR_FILE_EOF):
        resource = loader.get_resource()
        resource = resource.instance()
        mainscn.add_child(resource)
        loader = null
        loadscreen.queue_free()
        return
    elif(err == OK):
        update_progress()
        return
    else:
        loader = null
        show_error("while pooling resource")
        return 

func update_progress() -> void:
    var progress = float(loader.get_stage()) / loader.get_stage_count()

    label.set_text("Loading.. " + loader.get_stage() as String + " / " + loader.get_stage_count() as String)

    tween.interpolate_property( loadscreen, 'animated_val',
                                progressbar.get_value(), (progress*100),
                                0.4, Tween.TRANS_QUAD, Tween.EASE_IN_OUT)
    if (!tween.is_active()):
        tween.start()
by (59 points)
selected by

Thanks for the detailed answer.

I'll look it over and try to figure out what works for me.

Thank you very much for putting this answer as the best one! Enjoy the singleton Hahah
if you need any help you can contact me on telegram/discord as PrometeoPrime

+1 vote

In my own opinion, managing multiple scene is easier with a main scene that never change and the others scene preloaded using the preload function. Then to simply instantiate them and add them as a child of the main scene. Don't forget to call queue_free on the scene instances no longer needed.

by (1,045 points)

Thanks for the input.

+1 vote

Better scene export variable:

export(PackedScene) var world_scene
by (4,209 points)

Thanks for the input.

I identified the problem in my code.

The Player script carries over into World2 but the WorldComplete script does not work.

I have been trying to implement the later as a Global script but have not got it to work yet.

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 Frequently asked questions and 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 [email protected] with your username.