So looking at the docs, I should be able to save a dictionary to with FileAccess.store_var. Below are two save and load functions taken from the docs.
So I take my player node and put it in a dictionary. It has a variable named pc_name which is “Jack” and another variable named pc_cat which is “Meow”. I then save the dictionary with FileAccess.store_var and open it with FileAccess.get_var. It seems to save the dictionary and load it.
var d = {}
player.pc_name = "Ruth"
player.pc_cat = "Woof"
d["player"] = $Player
prints(d.player.pc_name, d.player.pc_cat, player.pc_name, player.pc_cat)
save_to_file(d)
var d2 = load_from_file()
var p2 = d2.player
prints(p2.pc_name, p2.pc_cat, player.pc_name, player.pc_cat)
func save_to_file(content):
var file = FileAccess.open("user://save_game.dat", FileAccess.WRITE)
file.store_var(content, true)
func load_from_file():
var file = FileAccess.open("user://save_game.dat", FileAccess.READ)
var content = file.get_var(true)
return content
However, the output is:
Ruth Woof Ruth Woof
Jack Meow Ruth Woof
So, even though the variable is saved as “Ruth” and “Woof” it loads as “Jack” and “Meow”. Crazy stuff! Anybody else run into this bug?
When you are done loading or saving the file you should explicitly close it.
func save_to_file(content):
var file = FileAccess.open("user://save_game.dat", FileAccess.WRITE)
file.store_var(content, true)
file.close()
Otherwise if you save it and then immediately load it, Windows or whatever OS you are using may return your old file. (OS cache systems and flushing timings, something like that, meant to speed up IO)
This is not a bug at all, but expected behavior.
Doing $Player is just a reference to a node. But you are modifying the player variable. These are two different things. When you load, you simply load the Player node with default values. You’re not actually saving the player instance that you modified.
When a FileAccess object goes out of scope, it is automatically flushed and closed. This should not affect anything.
This is the correct answer, but for the wrong reasons. The code above correctly modifies the properties, correctly stores the node in the file, and correctly loads the node from the file.
The reason the name change doesn’t get saved is because Godot will not serialize custom properties unless you specifically tell it to. It will correctly save the Node, all the built-in properties you modify, as well as the path to the script attached. However, it will not save custom properties declared in the script. As such, when it loads the Node from the file, those will revert to default values.
If you want to modify what properties get serialized from a script, you can do so using Node._get_property_list(), although that’s a bit advanced for most use cases. In general, you can simply @export any property you want saved:
# This won't save the properties to the file
class_name Player extends Node
var pc_name: String = "Jack"
var pc_cat: String = "Meow"
# This will
class_name Player extends Node
@export var pc_name: String = "Jack"
@export var pc_cat: String = "Meow"
Thanks for the feedback gang. Since I posted that, I started reading up on the get_property flags which seem like a big pain. Glad it’s as simple as @export var.
For anybody else reading this, @export var any variables you want to save, store them in a dictionary, and use the above functions to save and load the dictionary. It seems like the simplest way to implement a save system in Godot.