Then i want to load that saved scene/file when i press load button
So i have this Code for save scene
var save_path := "user://save_data.json"
func save_scene():
var data := {}
var root = get_tree().current_scene
for node in root.get_children():
if node.has_meta("savable"):
data[node.name] = {
"position": node.position if "position" in node else null,
"other_data": node.get("data") if node.has_method("get") else null
}
var file := FileAccess.open(save_path, FileAccess.WRITE)
file.store_string(JSON.stringify(data))
file.close()
print("Scene saved.")
func load_scene():
if not FileAccess.file_exists(save_path):
print("No save file found.")
return
var file := FileAccess.open(save_path, FileAccess.READ)
var data := JSON.parse_string(file.get_as_text())
file.close()
if data == null:
print("Failed to load save data.")
return
var root = get_tree().current_scene
for node in root.get_children():
if node.name in data:
var saved = data[node.name]
if saved["position"] != null:
node.position = saved["position"]
if saved["other_data"] != null and node.has_method("set"):
node.set("data", saved["other_data"])
print("Scene loaded.")
But in the load script i get this error ,without even entering the game
For
JSON.parse_string(file.get_as_text())
Variant as typed type is not terribly useful, so it’s telling you you’re making a variable and typing it as a variant, because you’re inferring the type and the variable you’re inferring from is a variant.
I’m talking about static typing in GDScript. Honestly, it sounds like you’re following some tutorial without really having an understanding of how GDScript works.
Just remove the colon after "var data := " would be my first quick try.
Ok that worked thanks, i am very new to Godot,
the game doesn’t crash any more
and when i press Load
i get this
But how to save the main scene exactly?
Should i drag the main scene or player scene and put it somewhere here ?
var save_path := "user://save_data.json"
func save_scene():
var data := {}
var root = get_tree().current_scene
for node in root.get_children():
if node.has_meta("savable"):
data[node.name] = {
"position": node.position if "position" in node else null,
"other_data": node.get("data") if node.has_method("get") else null
}
var file := FileAccess.open(save_path, FileAccess.WRITE)
file.store_string(JSON.stringify(data))
file.close()
print("Scene saved.")
It looks like you’re following the Godot tutorial for saving in the docs. I do that and it works for some things, but only if they are not too complex. I cannot get JSON.stringify() to encode and decode complex Godot objects. It works with Arrays, Dictionaries and primitives well. For saving scenes or nodes, I recommend creating a resource to save all the info and using ResourceSaver.save().
hi! I tried this let’s say I want to save the current player position only for now,
I did this
func save_position():
var file = FileAccess.open("user://player_save.json", FileAccess.WRITE)
if file:
var data = {
"position": global_position # Or use position if not using global
}
file.store_string(JSON.stringify(data))
file.close()
func load_position():
if FileAccess.file_exists("user://player_save.json"):
var file = FileAccess.open("user://player_save.json", FileAccess.READ)
if file:
var data = JSON.parse_string(file.get_as_text())
if typeof(data) == TYPE_DICTIONARY and data.has("position"):
global_position = data["position"]
file.close()
func _on_save_pressed() -> void:
save_position()
print("saved")
func _on_load_pressed() -> void:
load_position()
print("loaded")
Seems like Godot’s JSON doesn’t store Vector2 in a way it can decode as Vector2, so it falls back to a String. Maybe you will have to store each x/y component, or use a different format than JSON like ConfigFile or a custom binary format using file.store_var and file.get_var.
This error states you are trying to get the global_position of something that doesn’t exist, in this case $player.global_position so your $player isn’t a direct child of this node.
so, I need to create $player.global_position or global_position node then attach it to my player script or something? do you how to fix this exactly i mean what the code should look like?
Your script doesn’t have “player” as a direct child, I do not know what your player is a child of, in another script you use %player which would use a unique name to find the player, maybe that’s what you need.
The fix depends on what your scene tree looks like. Usually typing the $ symbol will start the auto-complete system which you can pick a node from there.
I found a solution to the save and load with this code, but the problem is when i press load or load latest save , my player returns to the first position on the scene and not loading the last position of the player on the scene
saver.gd
class_name Globals
extends RefCounted
# Static variables with explicit types
var playerx: float = 0.0
var playery: float = 0.0
const MAIN: PackedScene = preload("res://main.tscn")
func check_file() -> bool:
return FileAccess.file_exists("user://save_data.dat")
func savedata(playerx: float, playery: float) -> void:
var save_content: Dictionary = {
"playerx": playerx,
"playery": playery,
}
# Update global variables
self.playerx = playerx
self.playery = playery
var file: FileAccess = FileAccess.open("user://save_data.dat", FileAccess.WRITE)
if file:
file.store_line(JSON.stringify(save_content))
file.close()
else:
push_error("Failed to open save file for writing")
func load_data() -> Dictionary:
if FileAccess.file_exists("user://save_data.dat"):
var file: FileAccess = FileAccess.open("user://save_data.dat", FileAccess.READ)
if file:
var content: Variant = JSON.parse_string(file.get_line())
file.close()
if content is Dictionary:
# Update global variables with type-safe access
if content.has("playerx") and content["playerx"] is float:
playerx = content["playerx"]
if content.has("playery") and content["playery"] is float:
playery = content["playery"]
return content
else:
push_error("Invalid save file content")
return {"playerx": 300.0, "playery": 50.0}
else:
push_error("Failed to open save file for reading")
return {"playerx": 300.0, "playery": 50.0}
# Default fallback if file doesn't exist or is invalid
savedata(300.0, 50.0)
return {"playerx": 300.0, "playery": 50.0}
main.gd
extends Node2D
@onready var sea_sound_effect: AudioStreamPlayer2D = $SeaSoundEffect
@onready var winter_wind_sound_effect: AudioStreamPlayer2D = $ice_land/WinterWindSoundEffect
@onready var player = preload("res://player/player.tscn").instantiate()
@onready var main = preload("res://main.tscn").instantiate()
@onready var control: Control = $Control
@onready var save_load_control: Control = $Control # Make sure this node exists!
const SAVE_PATH = "user://savegame.cfg"
var player_position = Vector2(400, 100) # Default position
func _ready() -> void:
# Load initial position
player.position = player_position
# Connect buttons (assuming they emit signals)
if save_load_control:
if save_load_control.has_signal("saveit"):
save_load_control.saveit.connect(_on_save_requested)
else:
push_warning("'saveit' signal missing in Control node!")
if save_load_control.has_signal("loadit"):
save_load_control.loadit.connect(_on_load_requested)
else:
push_warning("'loadit' signal missing in Control node!")
else:
push_error("Control node not found!")
# Called when "Save" button is pressed
func _on_save_requested() -> void:
save_data(player.global_position)
# Called when "Load" button is pressed
func _on_load_requested() -> void:
load_data()
get_tree().reload_current_scene() # Optional: Reload scene to apply changes
# --- Save/Load Logic ---
func save_data(position: Vector2) -> void:
var config = ConfigFile.new()
config.set_value("player", "position_x", position.x)
config.set_value("player", "position_y", position.y)
if config.save(SAVE_PATH) != OK:
push_error("Failed to save game!")
func load_data() -> void:
var config = ConfigFile.new()
if config.load(SAVE_PATH) == OK:
player_position.x = config.get_value("player", "position_x", 400)
player_position.y = config.get_value("player", "position_y", 100)
else:
push_warning("No save file found. Using default position.")
globals
extends Node
var playerx=400
var playery=100
func set_data(plx,ply,st):
playerx=plx
playery=ply
extends Control
signal saveit
signal loadit
func _on_save_button_pressed():
saveit.emit()
func _on_load_button_pressed():
loadit.emit()
Seems like you have several save/load functions, and several globals. It may be a good first step to reduce your scripts to only the important functions.
It doesn’t seem like you ever set player_position or playerx/y so you will only save and load the default positions.
extends Node
const SAVE_FILE = "user://player_save.dat"
var player_position: Vector2 = Vector2.ZERO
var current_scene: String = ""
func save_game():
var save_data = {
"position": player_position,
"scene": current_scene
}
var file = FileAccess.open(SAVE_FILE, FileAccess.WRITE)
if file:
file.store_var(save_data)
file.close()
else:
print("Error saving game: ", FileAccess.get_open_error())
func load_game():
if FileAccess.file_exists(SAVE_FILE):
var file = FileAccess.open(SAVE_FILE, FileAccess.READ)
if file:
var save_data = file.get_var()
file.close()
player_position = save_data["position"]
current_scene = save_data["scene"]
return save_data
else:
print("Error loading game: ", FileAccess.get_open_error())
return null
player
func _ready():
# Load saved position if available
SaveSystem.load_game()
if SaveSystem.player_position != Vector2.ZERO:
position = SaveSystem.player_position
func _exit_tree():
# Save position when player leaves the scene
SaveSystem.player_position = position
SaveSystem.current_scene = get_tree().current_scene.scene_file_path
SaveSystem.save_game()