i have problem in making a save system

Godot 4.3

i have problem in making a save system i am tried many ways but nothing happened

if anyone have script and worked with him please help me

Well, if you ask me, building a save system every time is very old-fashioned and taking the time to design it slows down the development process a lot, so:

If you want to save the player’s state in the game:

Install GProgress plugin and just:

GPro.save_progress(user_id, game_state) #save game
game_state = GPro.load_progress(user_id, progress_id) #load game

Or if you just want to save the files:

Install SLib and just:

SLib.save_file(path, value) #save file
your_var = SLib.load_file(path) #load_file

iam sorry can you tell me the steps i should do

How to install here

Hot to use here

You have to show your script for us to help, aka what you have tried. You also need to explain what you expected to happen versus what really happened (any errors)?

This appears to be a duplicate of this thread you started save file system , but with less information.

1 Like

FileManager

extends Node

var SAVE_PATH = “user://savegame.data”

Save game data to a file

func save() → Dictionary:
return {
“total_coin”: Event.total_coin,
“level_data”: Event.level_data
}

Save the game to the file

func save_game() → void:
var save_file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if save_file:
save_file.store_var(save())
save_file.close()
print(“Game saved successfully.”)
else:
print(“Error: Could not open save file for writing.”)

Load the game data from the file

func load_game() → void:
if not FileAccess.file_exists(SAVE_PATH):
print(“No save file found, creating new save.”)
save_game()
return

var save_file = FileAccess.open(SAVE_PATH, FileAccess.READ)
if save_file:
    var saved_data = save_file.get_var()
    save_file.close()

    if saved_data.has("total_coin"):
        Event.total_coin = saved_data["total_coin"]
    else:
        Event.total_coin = 0

    if saved_data.has("level_data"):
        Event.level_data = saved_data["level_data"]
    else:
        Event.level_data = {}

    print("Game loaded successfully.")
else:
    print("Error: Could not open save file for reading.")

Auto-save when closing the game

func _notification(what):
if what == NOTIFICATION_WM_CLOSE_REQUEST:
save_game()

GameManager

extends Node

@onready var points_Label = $“…/UI/Panel/points”
@export var hearts: Array[Node] # Array of heart nodes for lives display
var total_collectables = 0
var curr_level: int = 0
var points = 0
var lives = 3

var is_ui_ready = false # Flag to check if UI is ready

Function to initialize the game state

func _ready():
# Set the flag indicating UI is ready
is_ui_ready = true

Function that runs every frame to check if UI is ready

func _process(delta):
if is_ui_ready:
# Load game data and set points from saved data
FileManagerSingleton.load_game()
points = Event.total_coin

    # Update the points display in the UI
    if points_Label:
        points_Label.text = "Points: " + str(points)
    else:
        print("Error: points_Label is null! Check the node path.")
    
    # Set the flag to false so it only runs once
    is_ui_ready = false

Function to decrease health when the player loses a life

func decrease_health():
lives -= 1
print("Lives remaining: " + str(lives))

# Update heart icons based on remaining lives
for h in range(3):
    if h < lives:
        hearts[h].show()
    else:
        hearts[h].hide()

# Reload the current scene if lives reach zero
if lives == 0:
    print("Game Over!")
    get_tree().reload_current_scene()

Function to add a point when the player collects an item

func add_point():
GameData.points += 1 # Increment points in GameData

# Update points display in the UI
if points_Label:
    points_Label.text = "Points: " + str(GameData.points)

# Update total coin count in Event and save the game
Event.total_coin = GameData.points
FileManagerSingleton.save_game()  # Auto-save after collecting points

# Check if all collectables are collected and emit signal
if GameData.points == total_collectables:
    emit_signal("all_collectables_collected")  # Trigger level completion

Function to set the total number of collectables for the level

func set_total_collectables(count: int) → void:
total_collectables = count

Function to change to the next level

func change_level(next_level: int):
curr_level = next_level # Update current level
Event.level_data[next_level] = false # Unlock next level
FileManagerSingleton.save_game() # Auto-save before level change

# Change scene to the next level
get_tree().change_scene_to_file("res://scenes/levels/level_" + str(next_level) + ".tscn")

Finish Area

extends Area2D

@export var target_level: PackedScene
@export var level_index: int # Identifies the current level

@onready var game_manager: Node = %GameManager # Reference to the Game Manager

When the player reaches the finish area

func _on_body_entered(body):
if body is CharacterBody2D:
# Unlock the next level (make sure next level exists in level_data)
if Event.level_data.has(level_index + 1):
Event.level_data[level_index + 1] = false # Unlock next level
FileManagerSingleton.save_game() # Auto-save progress
get_tree().change_scene_to_file(“res://scenes/hud.tscn”) # Move to the next level

HUD

extends Control

@onready var scene_tree = get_tree()
@onready var level_complete: TextureRect = $LevelComplete
@onready var game_manager: Node = %GameManager # Reference to the Game Manager
@onready var coin_count_label = $“…/UI/Panel/points” # Assuming “points” is a label to display points

var total_collectables = 0 # Track total number of collectables
var coin_count = 0 # Coin count initialized

Called when the level is completed

func _ready() → void:
Event.level_completed.connect(_on_level_completed)

Display level complete screen

func display_level_complete() → void:
if Event.curr_level >= Event.level_data.size():
$LevelComplete/TextureRect/HBoxContainer/NextLevelButton.visible = false
scene_tree.call_group(“buttonContainer”, “hide”)
level_complete.visible = true

Handle level completion event

func _on_level_completed():
coin_count = game_manager.get_coin_count() # Assuming GameManager manages coin collection
$LevelComplete/TextureRect/MoneyTexture/MoneyLabel.text = str(coin_count)
Event.total_coin += coin_count
var next_level = Event.curr_level + 1
print(Event.level_data)

if Event.level_data.has(next_level):
    Event.level_data[next_level] = false  # Unlock next level

FileManagerSingleton.save_game()  # Save after level completion
display_level_complete()

Go back to the home menu

func goto_home() → void:
get_tree().change_scene_to_file(“res://scenes/menu/main_menu1.tscn”)

Go to the level select screen

func goto_level_select() → void:
get_tree().change_scene_to_file(“res://scenes/menu/Level select/levelselect.tscn”)

Go to the next level

func goto_next_level() → void:
var next_level := “res://scenes/levels/level_” + str(Event.curr_level + 1) + “.tscn”
if FileAccess.file_exists(next_level):
Event.curr_level += 1
get_tree().change_scene_to_file(next_level)

Level Select

extends Control

@onready var scene_tree = get_tree()
@onready var moneylabel = $MoneyTexture/Label
@onready var level_grid = $TextureRect/LevelGrid

func _ready():
FileManagerSingleton.load_game() # Load saved data
connect_level_selected_to_level_box()

# Set level data or initialize the levels for the first time
if Event.level_data.is_empty():
    setup_level_box()  # First-time setup for locked levels
else:
    for box in level_grid.get_children():
        box.level_num = box.get_index() + 1
        # Unlock levels based on saved data
        box.locked = Event.level_data.get(box.level_num, true)  # Default to locked if no data

moneylabel.text = str(Event.total_coin)

Setup level buttons initially (all locked except the first)

func setup_level_box() → void:
if level_grid:
for box in level_grid.get_children():
box.level_num = box.get_index() + 1
box.locked = true # Lock all levels initially
level_grid.get_child(0).locked = false # Unlock the first level

Handle level selection and change scene accordingly

func change_to_scene(level_num: int) → void:
# Ensure the level data reflects the correct state before transitioning
Event.curr_level = level_num
scene_tree.change_scene_to_file(“res://scenes/levels/level_” + str(level_num) + “.tscn”)

Connect level selection signals to corresponding functions

func connect_level_selected_to_level_box() → void:
if level_grid:
for box in level_grid.get_children():
box.connect(“level_selected”, change_to_scene)

Handle home button press and change to the main menu

func _on_home_button_down() → void:
scene_tree.change_scene_to_file(“res://scenes/menu/main_menu1.tscn”)

Handle when a level is completed (in some game logic, e.g., in a GameManager)

func on_level_completed(level_num: int) → void:
# Unlock the next level after completing the current one
if Event.level_data.has(level_num):
Event.level_data[level_num] = false # Unlock the level

# Update the `locked` state of the level button
for box in level_grid.get_children():
    if box.level_num == level_num + 1:  # Unlock the next level
        box.locked = false

# Save the updated game state
FileManagerSingleton.save_game()  # Save progress after level completion

Level Box

@tool
extends TextureButton

signal level_selected

@export var locked: bool = true:
set(value):
# Avoid recursion by directly setting the locked status and calling appropriate functions
if locked != value:
locked = value
if locked:
level_locked()
else:
level_unlocked()

@export var level_num = 1

func level_locked() → void:
level_state(true) # Disable button and show label for locked state

func level_state(value: bool) → void:
disabled = value # Button is disabled when locked
$Label.visible = not value # Show label when locked (hidden when unlocked)

func level_unlocked() → void:
level_state(false) # Enable button and set label text when unlocked
$Label.text = str(level_num)

func _on_pressed() → void:
if not locked: # Only emit signal if the level is unlocked
level_selected.emit(level_num)

Event

extends Node

Store completed levels

var completed_levels = {} # Store completed levels (e.g., {1: true, 2: false})
var total_coin = 0
var level_data = {} # Add level data if needed
var curr_level = 1 # Default starting level

This signal will be emitted when a level is completed

signal level_completed

This function marks a level as completed

func mark_level_completed(level: int) → void:
completed_levels[level] = true
# Unlock the next level
level_data[level + 1] = false # Set the next level to unlocked (false)
emit_signal(“level_completed”)
save_game()

Save the game data

func save_game() → void:
# Use FileManagerSingleton to save the game state
FileManagerSingleton.save_game()

Load the game data

func load_game() → void:
# Check if the save file exists
if FileManagerSingleton.file_exists(“res://path_to_save_file”):
# If the save file exists, load the saved game data
FileManagerSingleton.load_game()
else:
# If no save file exists, initialize the level data and coins
level_data = {1: false, 2: true} # Example: level 1 unlocked by default, level 2 unlocked after level 1
total_coin = 0 # Initialize total coin to 0 (or any starting value)
# Optionally save the initial data
save_game()

this is the codes the error when finish level 1 level 2 didnt work

Make sure to format your pastes, if it’s a long script maybe you can trim the paste to relevant parts or highlight where the error occurs

Can you share the errors you get? Looks like at most 11 in the Debugger panel.


Seems like you are setting locked here, sounds correct enough to me, does your box have a locked setter? can you print Event.level_data before this for loop is run?


If this is run it seems like it will always load the default level data, since the file “res://path_to_save_file” will never exist.

no error appear when i finish level 1 level 2 should unlocked

it didnt happen

this is the file

# in finish.gd
if Event.level_data.has(level_index + 1):
	Event.level_data[level_index + 1] = false  # Unlock next level

Your level complete checks if the next level exists before unlocking it, it doesn’t exist in the level_data because you give it a empty dictionary on start, no level data exists, so it will never unlock the next level.

Also none of your finish nodes have their level index set correctly.

The level select should check for locked nodes like this, instead of using the not operator

box.locked = Event.level_data.get(box.level_num, true)

The save file was never the issue, make sure to use prints or breakpoints to debug your project. I found these issues by print(Event.level_data) at various points like I asked in this post

Event.level_data before level select: { 1: true }