Skip to content

Saving games: simpler approach which allows saving non-scene nodes #11366

@jkulawik

Description

@jkulawik

Your Godot version:
v4.5.stable.flathub [876b29033]
URL to the documentation page (if already existing):
https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html
Issue description:

I am using the save system from the tutorial (with several QoL improvements... but that's a topic for another issue) and I have been looking for a way to save states of nodes which aren't scenes - as that is a requirement in the system from the docs. My specific case has to do with nodes created by func_godot, but the reason why I can't save those as scenes is not that relevant to this issue.

My solution is to save a dictionary where the keys are the nodepaths and the values is the data provided by the node, rather than an array of dictionaries where the parent path and name of the node are only stored inside.

While loading, instead of preemptively deleting everything in the persist group, we now once again get the nodes from the group and then check the save dictionary for each node's data, and only delete if the save does not contain the path.

Here's a snippet from my code to illustrate:

for node in get_tree().get_nodes_in_group("persist_static"):
		if node.get_path() not in level_data["nodes_static"]:
			node.queue_free()
		if not node.has_method("load_save_data"):
			push_warning("Static persistent node '%s' is missing a load_save_data() function, skipped" % node.name)
			continue
		node.load_save_data(level_data["nodes_static"])

# Wait for missing persist_static nodes to properly delete
await get_tree().process_frame

As far as I can tell, this approach has the same properties as the one from the docs:

  • if something was freed during gameplay (e.g. you picked up an item), it won't be in the save file so the original object will still get deleted during loading
  • if new nodes are added to a level in a game update, they will still not appear after loading because they're not in the save file (and thus get deleted).
  • both approaches rely on node tree paths

It's essentially the same system, but you're not deleting scenes a priori and reinstancing them - instead you rely on what's already in the level scene and only delete if you don't have the extra data in the savefile.

The potential benefits of this approach are:

  • primarily, it is not restricted to scene instances
  • simpler - we don't need to reinstantiate and find parents, the process might be simpler to understand
  • unlike in the current system, if a node is deleted from a level in a game update, it will not get reinstanced by the save data. Whether this is universally good is up for debate I suppose.
  • The docs mention an issue of the proposed system: persistent nodes cannot be children of other persistent nodes. This is not the case for the system I propose, as we rely on the nodes already being in the current scene and just looking up their data.

The only drawback I came up with is this: both approaches will break if you shift around the tree structure between updates, but in the current one it is at least possible to add the "orphan" node from the save as a child of the current scene. In the proposed approach, that node is simply gone. Doing something like this would be unadvised and hard to account for in any save system though, so I'm not sure how much attention should be paid to this.

AFAIK this would require not only rewrites of the doc, but also of the example project. But one thing at a time - first I'd like to discuss if this approach might have any hidden pitfalls. I have done this in my game and it seems to work fine, but I wasn't testing for edge cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions