Search code examples
godotscenepersist

How to make the functions done for group nodes in a scene not change after changing scene?


I have put nodes in a group. And I have put a func on the scene script to make changes to the nodes in group. In that func,I made the nodes to queue free and stuffs like that. But when I change scene and come back to the previous scene,the queued free is back again,it is not queued free anymore. How do I make it not change even after changing scene?


Solution

  • Why information is lost

    When you change the current scene with change_scene or change_scene_to the current scene is unloaded, and the new one is loaded and instanced.

    Perhaps it helps to conceptualize that the instance of the scene is not the same as the scene in storage. So what happens is like opening a file in an editor, modifying and not saving it.


    Alright, that is one solution if you really want to go that route: you can save a scene.

    First create a PackedScene, for example:

    var packed_scene := PackedScene.new()
    

    Then tell it to pack the root node of the scene, for example:

    packed_scene.pack(self)
    

    Note: any Nodes that don't have their owner property set to the Node you passed here will be ignored. And no, add_child does not set the owner property.

    If you have been paying attention, you know you can give a PackedScene to change_scene_to... But how do you keep it around when the scene changes?

    There are a few ways. And the thing is: if we can keep information around, we might not need to save the scene.


    Keep things around in autoloads

    The scripts and scene you add to your autoloads (singleton) in project settings stay there (unless you explicitly remove them) even when you change scene.

    Thus, a very simple way to keep information around is to have a script with some variables that you can write and read from anywhere in your project. And you can do that with an autoload.


    Keep things around in resources

    Godot caches resources. When you load the same resource in multiple places, you actually get the same object.

    Well, you know you can create a custom Resource class. To do that, on the context menu of the FileSystem panel select "New Script", and in the script make a class that extends Resource, and give it a class_name.

    Then you can create resources of that Resource from the context menu of the FielSystem panel by selecting "New Resource" and picking your Resource class when asked.

    Everywhere you load one of those, you are going to get the same object. Yes, even if it is in another scene. So you can add variables to your Resource class, write them in one scene and read them in another.

    I explain a more concrete example elsewhere.


    Keep things around in storage

    You can write to a file and read from a file. For example you can do this:

    # save
    var file = File.new()
    file.open("user://a.sav", File.WRITE)
    file.store_pascal_string(var2str(data))
    file.close()
    
    # load
    file.open("user://a.sav", File.READ)
    data = str2var(file.get_pascal_string())
    file.close()
    

    Or, if what you want to store is a Resource (be it a PackedScene, or some other Resource class including a custom one), you can use ResourceSaver and ResourceLoader:

    # save
    ResourceSaver.save("user://a.tres", resource)
    
    # load
    resource = ResourceSaver.load("user://a.tres")
    

    Of course, you can also load resources with load, or preload. You may also be interested in Background Loading.


    By the way, if you are going to save player progress, having all the player progress data in a single object makes sense. And if you are going to have all the data you are keeping all the player progress data in a single place, it makes sense it would be somewhere it is accesible all the time and stays around even when you change scenes, thus: put the player progress data in an autoload. Bonus: you can put the functions to save and load to a file there too.


    Don't change scenes

    Since changing scenes brings problems - consider another solution: don't.

    You can load an scene:

    var packed_scene := load("res://scene.tscn") as PackedScene
    

    Then make an instance of it:

    var scene := packed_scene.instance()
    

    Then add it to your current scene tree:

    add_child(scene)
    

    Yes, it is a Node! It also means you can…

    scene.queue_free()
    

    Or you can simply remove it with remove_child which does not free it, so you can add it back later.

    So you would be in control of what gets loaded or unloaded and when. Which is useful to keep stuff around (e.g. the UI, the player character, etc...). A drawback of doing it the way I describe here is get_tree().current_scene would not be useful to you anymore. See also Change scenes manually.