How do you save variables to save files?

Godot Version

v4.2.2

Question

I’ve been looking all over the internet, at youtube, reddit, and the godot wiki, but I can’t find anyone explain this in a way I understand. I just want to know how you store simple variables like int and bool to a save file. Can you store multiple variables to one file, or you can’t and that’s why noone’s explaining how to store single simple variables?? I don’t want to store an entire dictionary or object, I just want to be able to store a few variables of my choice, whatever I need. Like bools for settings, an int for a level, a list of strings and vector2s maybe for a 2d map, Idk I just can’t figure out how to save and load. Do I need to make a file manually? Does Godot make one automatically?

I’d really recommend reading this:

Ina way it doesn’t matter what or how much data you want to store, the process will be very similar.

2 Likes

Thanks, I’ll look into this now :+1:

Ok, I’ve looked into it and got “working” code, but it doesn’t save anything and it doesn’t make a save file. I tried going into godots saves and making a file manually but that also didn’t work. How do I make save files, save to it, and load? I followed what I saw in the guide under ConfigFile and pretty much redid everything minus the error detection, and while using print and keybinds for save and load nothing works.

Can you post your script? Might be a good idea to include the error checking.

I have this script under the player node, though it’s not actually a player node. Regrettably the game I have in mind requires a stupid amount of complicated stuff like saving, tilemap communication and editing with code, ect before even basic movement can work :stuck_out_tongue: (dangit godot wiki I told u not to use the emoji >:| )

extends Node
var pldir = 1
@onready var plsprite = $Playersprite
var tspath = "user://testsave.ini"

# Called when the node enters the scene tree for the first time.
func _ready():
	#plsprite.play("IdleNorth")
	pass

#func verify_save_directory(path: String):
#	DirAccess.make_dir_absolute(path)

func loadit():
	var conf := ConfigFile.new()
	
	pldir = conf.get_value("rot", "rrot", 1)
	print("loaded" + str(conf.get_value("rot", "rrot", 1)))
func saveit():
	var conf := ConfigFile.new()
	
	conf.set_value("rot", "rrot", pldir)
	print("saved" + str(pldir))

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	
	if Input.is_action_pressed("Placeholder button"):
		saveit()
	if Input.is_action_pressed("lolz"):
		loadit()
	if Input.is_action_pressed("North"):
		pldir = 1
	if Input.is_action_pressed("East"):
		pldir = 2
	if Input.is_action_pressed("South"):
		pldir = 3
	if Input.is_action_pressed("West"):
		pldir = 4
	
	if pldir == 1:
		plsprite.play("IdleNorth")
		plsprite.flip_h = false
	if pldir == 2:
		plsprite.play("IdleEast")
		plsprite.flip_h = false
	if pldir == 3:
		plsprite.play("IdleEast")
		plsprite.flip_h = true
	if pldir == 4:
		plsprite.play("IdleNorth")
		plsprite.flip_h = true

Me trying to figure out how to do this:

Your script is missing the vital save and load functions, notice you never used the tspath variable? They aren’t just for error checking, both functions happen to return an error condition that may or may not have happened, but they do the actual saving to file and loading from file.

func loadit():
	var conf := ConfigFile.new()

	var error := conf.load(tspath)
	if error == OK:
		pldir = conf.get_value("rot", "rrot", 1)
		print("loaded" + str(conf.get_value("rot", "rrot", 1)))
	else:
		print("Couldn't load tspath: ", error_string(error))
	


func saveit():
	var conf := ConfigFile.new()

	conf.set_value("rot", "rrot", pldir)

	var error := conf.save(tspath)
	if error == OK:
		print("saved" + str(pldir))
	else:
		print("Error saving tspath: ", error_string(error))
2 Likes

I don’t actually understand what this means though.

var error := conf.load(tspath)

I would read this as
“make variable” “name1” “set to” “name2” “ignore the prior variable, instead get the property of it” “referring to this part of name2”

But you’re saying it’s a function? Are you saying this calls a function instead of referring to a part of the variable? I’ve used “variable.alterationtovariable” kind of stuff, like inverting a value stuff, but I haven’t seen a (method?) of a variable call anything or change anything before. Am I missing something about how this works?

I tried replicating your code but don’t know why declaring a variable gets loading and saving to work. I mean it works but I’d like to know why too, not just how

conf.load(tspath)

This part is the function, functions can return values which can be stored in variables. Even ConfigFile.new() is a function, you store it’s result (a new config file object) in the variable conf.

func example_function() -> int: # returns an integer
    return 4

var value := example_function() # storing the integer
print(value) # prints 4

In the case of conf.load(tspath) the function tries to load the file into your conf object, if it succeeds it will return OK otherwise it will return the relevant error code, maybe ERR_FILE_NOT_FOUND (aka 7)

1 Like

Ohhh thank you so much. I didn’t know the .thing() 's were functions. I thought they were the inbuilt ones (_ready(), _process(delta), ect) and custom ones (saveit(), ect) .

What I think I’m getting here is

Thing.thing = method
Thing.thing() = function (mini function? method-function?)

Is that right? (I also didn’t know that function-methods could call things outside of it’s immediate declaration, I don’t think I actually understood how .new() functioned)

the assignment operator = sets the left hand side to the value of the right hand side.

left = right

function calls are present anytime you see parenthesis (). They aren’t related to assignment and do not have special names in relation to assignment. They can appear on either side of an assignment expression because they can return values.

1 Like

hmm, I get that, but I don’t get why
var error := conf.load(tspath)
makes “error” return an int relating to the error, but then also seems to run extra code not relevant to the operation and loads/saves data from a folder. I’m thinking this is just an exception because saving/loading must be messy behind the scenes, or I just severely misunderstand how operations in godot work.

I’d expect something like

Manage_Save.load(conf,tspath)

to load stuff, but the code looks more like

var countnum = playername.length

Does this mean saving/loading requires altering a variable to make the code respond?

The code it runs is relevant, you can call it without keeping the return value

conf.load(tspath)

But now if it failed to load the file you have no way of knowing that happened, so calling conf.get_value() could cause more errors and return invalid values. This load function isn’t reliant on you creating var error it’s just a really good idea, like with ConfigFile.new(), it doesn’t need to be stored in a variable, but it’s pretty useless on it’s own.


It does alter the conf object, it isn’t a pure function. The left hand side of the dot is relevant to the function call, in your eample Manage_Save.load(conf,tspath) “Manage_Save” is a relevant part of the function.

A ConfigFile object is a lot like a dictionary, when you make a new one it’s empty. You can add things to it, but it doesn’t persist when restarting. So you must call a function to load and save files.


You might be confused seeing functions without a dot like saveit(), but that’s because Godot implicitly calls properties and functions on self.

saveit()
# same as:
self.saveit()

pldir = 4
# same as:
self.pldir = 4

The only truly global functions are math-related pure functions and prints, listed here.

1 Like

Thanks for taking the time to explain, I think I get it now. : D (dangit wiki u make me put spaces in my faces.)

And I meant that I was confused on how calling a math operation also ran the function in ways that didn’t correlate directly with the immediate operation. But it is interesting the bit on Godot calling “self” on it’s own.

Thank u so much for the help, I’ve been stuck on this for the entire day and yesterday, midnight now lol :+1:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.