How to save and load game in Godot Engine

Saving and Loading player progress is a requirement for every game. In this post, we will learn how to save and load game in Godot Engine.

Godot Engine has a built-in data type called Dictionary. It is very efficient that we can map any object to any other object. We will use the Dictionary class to map string keys to variable values. Dictionary in Godot can be parsed to JSON string, so we can make use of that.

Create a Project

Create a project with an empty scene as shown below. It is a basic scene with just Node2D as the only child. I’ve renamed it to ‘game’ thinking, this is our real game scene.

godot empty game scene

We are not going to worry about any other project properties like width, height, scale mode etc. We are here now to implement save and load the game data.

In the Inspector, click on the New Resource button.

create new resource

Then in the opened window, choose GDScript. We are going to create a new gdscript which is used as a singleton for user data management. Let us proceed.

create gdscript resource

After creating, the script editor will open. Save the file into res://autoload/user_data_manager.gd.

Use the same process to create another script res://autoload/game_manager.gd.

Now go to Project Settings > AutoLoad.

Also Read:   How to automate Godot Android build process in 5 easy steps

Browse our two scripts and set their names as shown below.

set scripts as autoload

To save and load game in Godot engine, we need to create some scripts. We will create this once and can be used in all of our game. So let us jump into scripting.

User Data Manager and Game Manager scripting

The user_data_manager.gd script looks like,

extends Node2D

signal loaded
signal version_mismatch

const LOADED = "loaded"
const VERSION_MISMATCH = "version_mismatch"

var _path = "path/to/save/data.dat" #Change this to user://data.dat after testing

var _user_data = {}


func load_data(default_data, version):
    _user_data["version"] = version
    _user_data["data"] = default_data

    var f = File.new()
    if(not f.file_exists(_path)):
        save_data()
    f.open(_path, File.READ)
    var loaded_data = {}
    loaded_data.parse_json(f.get_as_text())
    _parse_loaded_data(loaded_data)
    f.close()

func _parse_loaded_data(loaded_data):
    var loaded_version = loaded_data.version
    for key in loaded_data.data.keys():
        _user_data["data"][key] = loaded_data["data"][key]
    if(loaded_version != _user_data.version):
        emit_signal(VERSION_MISMATCH,loaded_version,loaded_data.data)
    else:
        emit_signal(LOADED)

func save_corrected_data(corrected_user_data):
    _user_data["data"] = corrected_user_data
    save_data()

func update_version(version):
    _user_data["version"] = version
    save_data()

func save_data():
    var f = File.new()
    f.open(_path,File.WRITE)
    f.store_string(_user_data.to_json())
    f.close()

func set_data(key,value):
    _user_data["data"][key] = value

func get_data(key):
    return _user_data["data"][key]

To understand the above script we have to look into the game_manager.gd script.

extends Node2D

var _default_user_data = {
    score = 0
}

func _ready():
    UserDataManager.connect(UserDataManager.LOADED,self,"init")
    UserDataManager.connect(UserDataManager.VERSION_MISMATCH,self,"_user_data_version_mismatch")
    UserDataManager.load_data(_default_user_data,"0.0.1");

func _user_data_version_mismatch(loaded_version,loaded_data):
#	Check loaded_version, parse loaded_data and update the savedata
#	UserDataManager.save_corrected_data(corrected_data)
#	Then update the new version
#	UserDataManager.update_version("0.0.2")
    init()

func init():
    print("INIT")
    pass

Let us check what is the above script does.

Singleton in Godot is auto instantiated and available to every script by the name we set in the project settings. We have two Singleton objects now – UserDataManager and GameManager. Both can be accessed from any other script we are going to write for our game.

How to save and load games in Godot Engine Click To Tweet

We have a default user data variable. This is a dictionary object. If there is no save data present when the game starts, this default data is used. In fact, it goes deeper, even if the loaded data has no records for a specified key then the value from this default data is used. This is possible when we update our game and has additional values to be saved.

Also Read:   Creating a PHP website using MVC – 2 Creating the Controller and Template

When our game boots up, UserDataManager’s load_data method is called with the default data and a version string. This version string can be in any format but it should represent our game version somehow. The UserDataManager will load the save data from the disk. If there is no save data present, it creates a new one with the specified version. If our current game version is different than the one in the saved file, the version mismatch function is called. This is the place to write logic to convert the old user data format to the new one.

That’s it. We can initialize everything in the init() and UserDataManager will manage the saving and loading of our game.

This is how you save and load game in Godot Engine.

When we want to save any data we can call,

UserDataManager.set_data("health",75)
UserDataManager.set_data("coins",100)
UserDataManager.save_data()

And to get something,

var player_health = UserDataManager.get_data("health")

Calling the above methods will work from any script and returns the value from our saved data. The above script will only work on 2.1.x versions of Godot engine. Godot 3.0 is just around the corner and scripting API is different for the Dictionary class. We will check it out when it is released.

If you really need an explanation for the user_data_manager.gd, let me know in the comments below. Thanks for reading.

Never miss any content from Codetuto!

Subscribe to Codetuto Newsletter and be the first to recieve our latest posts and tutorials

Subscribe Now For Free

[Total: 4    Average: 3.3/5]
  • Junpei

    thanks a lot mate.
    i like that this tutorial is broad enough that it suits any godot game.

  • Lars Kokemohr

    You should avoid class names that end in “Manager”. It’s a phenomenon that I like to call “DuckManager typing”, based on the line “if it quacks like a duck, swims like a duck and flies like a duck I call it a DuckManager”.

    Objects don’t represent actors that do something to your data, they represent the actual data. So in many cases it’s just unnecessary to add the “Manager”. Your two classes represent the user_data and the game, not the user_data_manager or the game_manager.

    This becomes even more apparent when you look at the function names in the user_data_manager:
    user_data_manager.load_data()
    user_data_manager.save_data()
    user_data_manager.set_data(key, value)
    user_data_manager.get_data(key)

    The “data” in that code is redundant, there’s no real reason to not just write
    user_data.load()
    user_data.save()
    user_data.set(key, value)
    user_data.get(key)

    This is not just shorter to read, it also communicates better that the user_data-class is supposed to represent the user data. It’s not just a tool that operates on user data that’s stored in a different place. That’s important because it encourages encapsulation (which makes debugging easier and helps avoid bugs in the first place) and it helps keep your classes clean and easier to read (a common problem with manager classes is that they end up as a huge do-it-all class).

    • Vinod

      The UserDataManager had several other features at first managing several save data files. But I cut out all of unwanted things, I need to clean up it a bit.

      • Lars Kokemohr

        That’s kind of my point. To be clear, I am not trying to talk down on you, nor am I trying to be overly correct with meaningless OOP principles.
        DuckManagerTyping has just become a major issue in Unity tutorials, and there are good reasons to avoid that trap.

        Classes should always adhere to the single responsibility principle, i.e. they should do one thing only. The idea behind object orientation is to bundle functions together with the data they are manipulating, so that if a variable ends up wrong you only have to search the class its in for the faulty code.

        So if you have a class managing several sets of user data it should in fact literally contain a list of UserData instances that look like the code above, so that if something goes wrong during saving you can ignore the code that selects the correct UserData instance when searching for the bug.

        At the latest when you need to change the saving behavior of one type of user data a user data manager that doesn’t use instances of a user data class would immediately become more complex than it should. If you do use a user data class on the other hand the obvious choice is to create new subclasses with different storing behavior.

        At my first job we had a DatabaseManager class with more than 10.000 lines of code and I have seen many other examples, so I am talking from experience when I say that manager classes that don’t quite literally manage a collection of objects more often than not are the place where any code that is remotely related to a subject is dumped.

        By the way I offer free consultation for tutorial authors if you are interested: http://www.lkokemohr.de/godot_tutoring.html
        I don’t pretend to be a know-it-all nor am I trying to school you for my own ego, it’s just that if we find a way to improve your tutorials what my students learn from them will improve as well, so it’s a win for both of us.