2025/09/29
- Type
- Learning Resource
- Format
- Cheat Sheet
- Version
- Godot 4.x
- Subject Tags
- Quick reference
- Five saving methods compared
- Code examples
- Created
- Updated
- 2025/09/29
- 2025/09/29
This is a quick reference for the five main data-saving tools in Godot 4. Whether you're saving player progress or game settings, this cheat sheet will help you know which tools are available and pick one for your project.
Godot offers multiple saving methods, some of which are important to know, but not all are equally useful in practice.
Most of us mainly use just one or two approaches depending on our needs and preferences.
You can quickly scan the tools below to find the one that works for you without having to remember every detail.
In this cheat sheet, you'll find:
These two functions, var2str
and str2var
, convert any Godot data structure into a string and back. All the Godot-native text serialization and loading methods you'll see below use this under the hood.
You can directly use these functions to save and load data in a human-readable format. Here's an example of how to save and load a dictionary that keeps track of the player's health and inventory:
var data := {
"health": 100,
"inventory": [
{"name": "sword", "amount": 1},
{"name": "potion", "amount": 3}
]
}
var saved := var2str(data)
var file := FileAccess.open("user://savegame.txt", FileAccess.WRITE)
file.store_string(saved)
file.close()
And here's how to load it back:
var loaded_file := FileAccess.open("user://savegame.txt", FileAccess.READ)
var file_content := loaded_file.get_as_text()
loaded_file.close()
var loaded := str2var(file_content)
You probably won't use this method directly, but I'm including it because it's the foundation of Godot's text serialization, used by other methods like ConfigFile
and ResourceSaver
.
str2var
to load data from untrusted sources like in user-generated content, as it can execute arbitrary code. Only use it for files you fully control.The functions var_to_bytes()
and bytes_to_var()
work similarly to var2str()
and str2var()
, but they convert data to binary instead of strings. Godot's networking and multiplayer features use this under the hood, along with the save and load methods FileAccess.store_var()
and get_var()
you'll see below.
Here's a quick example of how to use them:
var data := {"score": 1000, "lives": 3, "power_ups": ["speed", "jump"]}
var bytes := var_to_bytes(data)
And to convert the bytes back to a variable:
var restored_data := bytes_to_var(bytes) as Dictionary
You probably won't use these functions directly, but rather call FileAccess.store_var()
and get_var()
which use them under the hood.
Unlike the string versions (var2str()
and str2var()
), there are "with_objects" variants for the binary functions that can serialize complete objects including their code.
The regular var_to_bytes()
and bytes_to_var()
functions are safe because they don't support object serialization and deserialization. In Godot, objects can contain resources and scripts, which means loading them can execute code. So be cautious when using the "with_objects" variants, especially with untrusted sources or when doing networking.
FileAccess.store_var()
is Godot's built-in binary serialization that works with any engine native data type (like strings, vectors, colors, etc.). At GDQuest, we use this method most often for games with relatively simple save data structures.
By default, this method uses the engine's safe binary serialization that's also used for online multiplayer, meaning it can't load any code (unlike other serialization and deserialization methods in Godot). It's a very safe and efficient way to store and load data because it only supports values, like JSON, and not executable code.
But unlike JSON, it natively supports Godot's Vector2, Vector3, Color, and other types, so there's less code to write and maintain. Plus the data is more compact and takes less space.
Here's how you save data with FileAccess.store_var()
:
var data := {"level": 7, "experience": 1500, "inventory": ["sword", "shield"]}
var file := FileAccess.open("user://savegame.data", FileAccess.WRITE)
if file != null:
file.store_var(data)
file.close()
You load it just as easily:
var loaded_file := FileAccess.open("user://savegame.data", FileAccess.READ)
if loaded_file:
var loaded = loaded_file.get_var()
loaded_file.close()
To save and load multiple data structures, you call store_var()
and get_var()
multiple times in the same file. Each call stores or loads one "variable" (one piece of data or data structure). You can save a dictionary, then an array, then an integer, etc.
I recommend it for: Any data that's not too complex where you don't need automated type safety or editor integration The trade-off: It uses binary data, so the data isn't human-readable or easily editable. It doesn't support saving and loading complete objects by default. You can enable that but this also enables loading code, which can be a security risk for user-generated content.
In the engine, resources are used to save and load lots of data including scenes, tilemaps, materials, particle shaders, etc. They're Godot's scriptable objects that can be easily saved to and loaded from files.
Resources aren't designed specifically for savegame data but they're very powerful and flexible so you can use them for that purpose. At GDQuest, this is our preferred method when we need structured, complex save data or want to take advantage of the editor to inspect and edit save files during development.
Here's a simple example of how to use resources to save and load nested, strongly-typed save data.
First, we define the resource types themselves. They're just scripts that extend Resource
, with an exported variable for each piece of data we want to strongly type and save. Our player has an inventory and we want to save each inventory item. We first create a resource for the inventory item:
class_name InventoryItem extends Resource
@export var name := "item"
@export var quantity := 1
@export var value := 0
We can create another resource to hold all the player save data, including an array of inventory items:
class_name PlayerSaveData extends Resource
@export var health := 100
@export var inventory: Array[InventoryItem] = []
Now you can save player data by creating instances of these resources. You can create the resources in the editor directly, or in code like this:
func create_save():
var sword := InventoryItem.new()
sword.name = "sword"
sword.quantity = 1
sword.value = 50
var potion := InventoryItem.new()
potion.name = "potion"
potion.quantity = 3
potion.value = 10
var player_data := PlayerSaveData.new()
player_data.health = 100
player_data.inventory = [sword, potion]
ResourceSaver.save(player_data, "user://savegame.tres")
To save any resource and all the sub-resources (like our inventory items), you just call ResourceSaver.save()
with the resource and the file path.
To load the saved data back, use ResourceLoader.load()
(or just GDScript's load()
function):
var loaded := ResourceLoader.load("user://savegame.tres") as PlayerSaveData
The file extension can be .tres
to output a text file or .res
for a binary output. Either way you can load the file the same way, so you can use text during development and switch to binary for release.
I recommend it for: Complex game data and anything that benefits from type safety Pros: Strongly typed data, editor integration, you can reference and save resources you use throughout your game codebase directly (like character stats) Cons: It's a bit more complex when you're getting started. Resources can contain embedded scripts, which means loading untrusted resource files can execute arbitrary code.
ResourceLoader
that prevents loading resources that contain scripts.There are more save methods in the engine that you can use if you like their API or file format. Here are two more that are worth knowing about.
The ConfigFile
class creates simple, readable configuration files using the classic INI format with sections and key-value pairs. This method organizes your data into sections like "Graphics" and "Controls" with key-value pairs under each.
Here's how to save data with ConfigFile:
var config := ConfigFile.new()
config.set_value("Graphics", "fullscreen", true)
config.set_value("Graphics", "resolution", Vector2i(1920, 1080))
config.set_value("Controls", "mouse_sensitivity", 2.5)
config.save("user://settings.cfg")
The first argument is the section name, the second is the key, and the third is the value.
Here's how to load it back:
var config_load := ConfigFile.new()
var error := config_load.load("user://settings.cfg")
if error == OK:
var fullscreen: bool = config_load.get_value("Graphics", "fullscreen", false)
var resolution: Vector2i = config_load.get_value("Graphics", "resolution", Vector2i(1280, 720))
var mouse_sensitivity: float = config_load.get_value("Controls", "mouse_sensitivity", 1.0)
When loading values, the third parameter in get_value()
is a default value that gets returned if the key doesn't exist in the file.
I recommend it for: Simple settings and configuration data, if you like the INI format (though I would personally favor resources)
var2str
and str2var
functions under the hood, so be cautious when loading data from untrusted sources. JSON is a widely used format that's usually supported by most programming languages. It's familiar to many developers and easy to read.
However, JSON is limited and doesn't natively support Godot-specific types like , Vector2, etc. You need to manually convert these types to and from JSON-compatible formats (like dictionaries or arrays). Another thing to be careful with is that because it's JavaScript's format, it doesn't support integers, only floats for numbers. So you have to be mindful of that when saving and loading data. Color
Here's how to save data with JSON. I've taken the same settings example as for config files. First, we convert the data to a JSON string:
var resolution := Vector2i(1920, 1080)
var data := {
"fullscreen": true,
"resolution": {
"x": resolution.x,
"y": resolution.y
},
"mouse_sensitivity": 2.5
}
var json_string := JSON.stringify(data)
Then we save the JSON string to a file:
var file := FileAccess.open("user://settings.json", FileAccess.WRITE)
if file != null:
file.store_line(json_string)
file.close()
And here's how to load it back and convert the JSON data back to Godot types:
var loaded_file := FileAccess.open("user://settings.json", FileAccess.READ)
if loaded_file != null:
var file_content := loaded_file.get_as_text()
loaded_file.close()
var json := JSON.new()
var parse_result := json.parse(file_content)
if parse_result == OK:
var loaded_data = json.data
var fullscreen: bool = loaded_data.get("fullscreen", false)
var resolution_data: Dictionary = loaded_data.get(
"resolution", {"x": 1280, "y": 720}
)
var resolution := Vector2i(resolution_data["x"], resolution_data["y"])
var mouse_sensitivity: float = loaded_data.get("mouse_sensitivity", 1.0)
I recommend it for: Applications that interact with web backends (for web requests) and when you want to export data for interchange with other applications The trade-off: It needs more work than Godot's built-in methods since it doesn't natively support Godot-specific types like or Vector2. You need to manually convert these types to and from JSON-compatible formats. Color
Overall it's a fine choice if you're used to the format and like using it. But it doesn't have any particular advantages over Godot's native methods for most game save data. FileAccess.store_var()
or resources are generally more efficient.
I've only included native tools that come with the engine in this cheat sheet. For large and complex projects, another potential approach is using an external database like an SQL database. There's a community-maintained GDExtension that you can use if you are experienced with databases and need the full power of an SQL database: Godot SQLite
The user://
path is a special folder that Godot gives you specifically to save user data like save games or player settings. You can also use it to download user-generated content if your game supports that. This folder is separate from your game project and is in a different location on each operating system.
When you make your game, you use the res://
path to access files that are part of your game itself, like scenes, scripts, textures, etc. These files get packaged with your game when you export it for release. But you don't want to save user data in the res://
folder because when you release your game, that folder becomes a single package file and you're not meant to write new files to it.
That's why Godot gives you the user://
path. It's a plain folder on the user's computer just for your player data. It's the right place for anything that changes during gameplay or that players should be able to modify.
You can see the exact location of the user://
folder by calling OS.get_user_data_dir()
in your code, for example if you need to tell players where their save files are stored.
Don't stop here. Step-by-step tutorials are fun but they only take you so far.
Try one of our proven study programs to become an independent Gamedev truly capable of realizing the games you’ve always wanted to make.
Get help from peers and pros on GDQuest's Discord server!
20,000 membersJoin ServerThere are multiple ways you can join our effort to create free and open source gamedev resources that are accessible to everyone!
Sponsor this library by learning gamedev with us onGDSchool
Learn MoreImprove and build on assets or suggest edits onGithub
Contributeshare this page and talk about GDQUest onRedditYoutubeTwitter…