Godot version:
official 3.1.1-stable_win64
OS/device including version:
windows 10 64 bit
Issue description:
My goal is it, to save nodes/objects in exactly the state they are currently in (including sub-objects etc.) by just calling one method, without having to manually define a save-method in every sub-object inside the node etc.
I know about the ability to save nodes and vars via inst2dict dict2inst and var2bytes and bytes2var and with the documentation tutorial of manually creating a dict that you use after loading to set up all the values inside the object and then separately dict2inst them on load and put the values.
My situation is, that my nodes has objects in vars in its script, which values get lost on load. Either via the values that get set in the _init of the objects or they dont get set at all (although they for example exist in the dictionary that gets produced by inst2dict). But this can get kind of solved by having inst2dict be done separately for every object inside of vars in the script.
So my requirement would be to be able to direclty save nodes in their current state and then successfully load them into the game again.
The thing is, i would be able to add load() and save() functions to those objects, but i actually plan on having a lot of children of one of my objects (Skill) and every object could have completely different behaviour, so i would have to define a lot of those load() and save() functions by hand. And i would prefer/expect there to be an easier way.
Does there currently exist a feature that could fulfil this requirement? If not, i would like to request the ability to directly save an Object/Node with all its sub-objects included into a file and successfully loading it in the exact state (including all sub-Objects inside it) at a later point.
Thank you for your time!
Steps to reproduce:
in the reproduction project just start the "Main.tscn" scene and click Spacebar to save and Escape to load.
My expectation would be, that on load it prints into the console: "I am now posting some stuff: 15" because i set it in the main-scene.
Then if you load an error should appear. This error can be fixed by adding the "Sub_Script.gd" into the _init() of the Player_node.gd but then the value would be reset (set to Null).
Minimal reproduction project:
Save_Test.zip
This is already possible although I wouldn't recommend it. You can save any branch of your scene tree as a scene at runtime which will save all the state of that branch and then load that scene and swap it from the original branch you had. That's simplifying it a bit but it does work. To save the scene you need to set all the children owner property to the "root" node you want to save. An example of saving a branch of a running scene:
func _ready():
var scene = PackedScene.new()
var scene_root = $Player
_set_owner(scene_root, scene_root)
scene.pack(scene_root)
ResourceSaver.save('user://SavedPlayer.tscn', scene) # or .scn to avoid people messing with it
func _set_owner(node, root):
if node != root:
node.owner = root
for child in node.get_children():
_set_owner(child, root)
Then you would load that scene var loaded_player = load("user://SavedScene.tscn").instance(), remove the old node you want to swap remove_child($Player) and add your loaded player add_child(loaded_player) (You can also check InstancePlaceholder to streamline that process)
I wouldn't recommend it because it removes some control over what to save and you'll save stuff that you may not want to be saved.
@mrcdk Thank you for the indepth explanation! I personally touched on the PackedScene way once, but it seemed to complicated and i didnt understood it fully. Your explanation makes it very clear to me!
An additional problem would be, that by saving the scene in its own file its open to manipulation (dont know if that matters though as the game is completely singleplayer)
Will try it out, but i would still prefer a simpler way to just save an object/node in a file. Something that i actually expected inst2dict and var2bytes to already do. But the instantiation behaviour of their respective methods dict2inst and bytes2var prevents it to work as expected IMO.
In java you would be able to properly do the serializing and deserializing with not big hassle too :/
I will give the packed-scenes a try (i have need of this only for one small part in my game, all the other systems already work properly with either defining a save_dict or inst2dict).
The reason i would want to save its state is: Its a skill-system that has skills as children (so they can _progress(delta) on their own) and i wanted to save if a skill was active/on cooldown when the game is being saved.
Alternatively i could just have him reset all the skills on load (reset cooldown, reset active, or just reset active).
Anyway: Thank you a lot for your help!
I provided some related info in https://github.com/godotengine/godot/issues/31045#issuecomment-517941647 (with some possible solution) and #6533 for more use cases on this.
Serialization can be a really non-trivial topic. Assuming that dict2inst/inst2dict should deal with everything "magically" is misguided IMO. What if you have objects with circular references to each other? What if some of your scenes are instanced, you serialize them with all properties, then in the next version of your game you'll change some of the Scene values and then loading the serialized ones will overwrite everything (since all the property dictionary was saved)? What about references to resources like Textures - perhaps you generate _some_ of them in the game itself (like avatar face with equipped helmet) and would like to save _some_ of them to disk, but not others? Etc, etc.
In my opinion, for any non-trivial software, full control over serialization is absolutely necessary. Some of your objects you will fully serialize, some (like an RPG skill learned by your character) you'll serialize only with skill scene path and skill level (for example). I think, no matter how universal of an serialization solution will be provided by the engine, you will always have some special cases. It actually is harder in games than in some business software because of all the graphic resources attached to objects.
Start with PackedScene and consider all of your objects case by case; try to simplify them, do not try to create super-universal solution, as it may very well complicate rather than simplify your game.
@dioptryk Thank you for that perspective, i didnt think of many of those corner cases yet.
But I didnt mean to imply that serialization is trivial. I meant that my expectations would be that the situation (sub-objects) im in would be easy to solve (nearly trivial)
Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine.
The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker.
If you are interested in this feature proposal, please open a new proposal on the GIP tracker following the given issue template (after checking that it doesn't exist already). Be sure to reference this closed issue if it includes any relevant discussion (which you are also encouraged to summarize in the new proposal). Thanks in advance!
Most helpful comment
I provided some related info in https://github.com/godotengine/godot/issues/31045#issuecomment-517941647 (with some possible solution) and #6533 for more use cases on this.