Identifying the same nodes between sessions

Godot Version

v4.5.stable.mono.official [876b29033]

Question

I have a feeling someone asked about it before, but I can’t find anything. The problem - I want to save state of certain nodes between sessions (examples: if coin has already been picked up, or a mob has already been killed), and in order to do that I need some kind of unique identifier that isn’t changed when scenes are changed/reset.

I wanted to ask if there is any straightforward approach to this, or maybe even built in solutions. My first idea would be to save initial coordinates and use it as identifier.

Do you mean more like a save data to load when saved game is loaded or more like runtime data to pass to a new scene?

1 Like

To be honest both. In my case upon player defeat I don’t want to fully reset the scene, but keep some parts of it the same. You can approach it from two sides: reset the scene, and restore what player already did, or don’t reset the scene, and restore some parts of the level to original state.
Also player can just quit the game, in this case I want to return them to the spawn point, but keep some of the things the same as when they left the game.

Now when I think about it, it might be the best to not keep it only in memory, but also instantly save it locally, in case game crashes (it doesn’t even need to be your fault, just issue with user’s PC). But either way, you need to identify which node is which. And every method of identifying nodes I have found only work during single session.

You can always identify by name.

Scenes can also be saved at runtime using PackedScene.pack() and ResourceSaver to save to disk. All properties that have their PROPERTY_USAGE_STORAGE flag set should save their runtime values. This flag is automatically set for all properties that are declared using @export annotation, or you can set it manually by implementing _get_property_list()

3 Likes

Using Name is probably going to be the safest. I didn’t really think about it at first because I thought node names are generated on runtime, but I can use editor name, which is constant. I will mark your comment as solution, but there is one more thing that I want to know. I’m placing some objects (scenes) that player can pick up through TileMapLayer. I’m testing if it works by printing their name in Ready(), and output is always the same, so it will work. But the names they output are kind of weird.

For example I have an ammo pickup with scene name PickupAmmo, and the output for this type of object looks like this:

PickupAmmo
@Area2D@7

Basically the first one always has the name of the scene, all subsequent ones use base node name + identifier.

This isn’t really an issue since player won’t see it, this is for internal identification, but I wonder why isn’t it just called PickupAmmo1, PickupAmmo2, PickupAmmo3, etc.

The engine does it to prevent the identical sibling names but doing it this way is a bit more efficient when the node count is large. There’s a second optional bool argument you can supply to add_child() called force_readable_name. Passing true should do what you want - assign a numerated scene name. The same will happen if you assign the name to the node after the node has been added to the tree.

2 Likes

I’m actually encountering an issue right now with identification by Name.

What I’m doing right now - there are coins scattered around the level, and when player dies I don’t want to reset the entire level, but keep the coins that player already has. When player dies I remove the level scene from the scene tree, and add the same level again to reset the state of the level scene.

– game
---- level (this is removed and added again)
---- player

When the level is restarted, every coin scene checks if it’s already in the list of collected coins. If it is, it gets removed.
And here is the issue - if I remove the level and add it again, coins get new set of distinct names, so identification completely fails (beside the first that is always called PickupCoin). When I turn off entire game and start again, then identification works.

I know that you said about force_readable_name, but all pickable objects are added through TileMapLayer, not through code. And I’m not sure if it would actually help, since I predict they would still get different names.

What do you mean by that?

If coins are tiles, you can identify them by cell coordinates.

1 Like

Coins are scenes, but TileMapLayer allows placing scenes as tiles (so you don’t have to drag and drop hundreds of scenes). Probably a new feature, since I have found out about it not so long ago.

I have changed it to my initial idea, meaning:

Id = $"{GlobalPosition.X}{GlobalPosition.Y}";

While it will work (there is always one object that can be picked up at single coordinate), it is not resistant to any changes (like moving coins around). But I suppose I can’t really do a lot of changes if I use TileMapLayer instead of manually placing scenes.

You don’t need to build an identifier. Just use cell coordinate Vector2i directly as the id.

1 Like