How to Save Game Data in Godot 4 Using JSON

Apr 5, 2025

Basic-Flow

Saving player progress is essential in nearly every game. In this tutorial, you'll learn how to create a basic save system in Godot 4 using JSON—a lightweight and readable data format supported natively by the engine.

We’ll cover:

  • 🧩 Structuring your data
  • 💾 Writing a save file
  • 📂 Loading saved data
  • 🔁 Reconstructing data like Vector2
  • ✅ Final tips and best practices

🧩 Why JSON?

JSON (JavaScript Object Notation) is easy to write, read, and parse—making it a great format for saving data like inventory, progress, or settings.

Godot 4 has built-in support for converting dictionaries to and from JSON strings, making it super convenient for game developers.

💾 Saving Data to a File

Here’s a basic function to save some example data:

func save_game():
    var save_path = "user://savegame.json"
    var file = FileAccess.open(save_path, FileAccess.WRITE)

    var save_data = {
        "player_position": [100, 200], # Vector2 as array
        "inventory": ["sword", "potion"],
        "gold": 50
    }

    var json_string = JSON.stringify(save_data)
    file.store_string(json_string)
    file.close()

    print("Game saved!")

📝 user:// points to your game's internal save folder on any device.

📂 Loading Data from a File

To restore saved data:

func load_game():
    var save_path = "user://savegame.json"

    if not FileAccess.file_exists(save_path):
        print("No save file found.")
        return

    var file = FileAccess.open(save_path, FileAccess.READ)
    var json_string = file.get_as_text()
    file.close()

    var result = JSON.parse_string(json_string)

    if result.error != OK:
        print("Failed to parse JSON.")
        return

    var save_data = result.result
    print("Loaded data: ", save_data)

🔁 Reconstructing Non-JSON Types

Since JSON doesn't support Godot-native types like Vector2, you’ll need to convert manually:

var pos_array = save_data["player_position"]
var player_position = Vector2(pos_array[0], pos_array[1])

This restores the original position.

✅ Tips & Best Practices

  • Check file existence before loading.
  • Store complex types like Vector2, Color, or Transform2D as arrays or dictionaries.
  • Consider encryption with open_encrypted_with_pass() for sensitive data.
  • Use a singleton (autoload) like SaveManager.gd to centralize save/load logic.
  • Add versioning in your save data structure in case you need to migrate later.

References