Save and load current scene

Godot Version

4,3

Question

Hi! i need help with this
, I want to save everything on the screen the
Player + position/location, the"main" scene with all its children/ nodes" etc

Basically the “main” scene that contain everything with it

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.")

When i enter the game and press Save

I get a message in the debug

‘Scene_saved’


And this code is for the loading

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())

What is this error how to fix it ?

This video is for godot 3
I want the same thing for my game! but without the stage 1 , stage 2, Only the main scene with all its children.

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.

You can either:

  1. don’t treat warnings as errors.
  2. don’t type data at all
  3. read the docs: JSON — Godot Engine (stable) documentation in English
1 Like

Blockquote

  1. don’t treat warnings as errors.
  2. don’t type data at all

but what i should type or do I can’t enter the game it crashes with this code

	var file := FileAccess.open(save_path, FileAccess.READ)
	var data := JSON.parse_string(file.get_as_text())
	file.close()

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.

  1. Just remove the colon after "var data := " would be my first quick try.
1 Like

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

Screenshot (634)

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().

1 Like

Do you have any nodes marked with metadata “savable”?

1 Like

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")

when I enter the game and press save I get this

but, if I press load
I get this error and the game crash!

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.

1 Like

I followed a tutorial step by step and did exactly like him

extends Node


func _on_save_pressed() -> void:
	var save_file = serializer.new()
	save_file.player.player_position = %player.global_position
	
	save_file.save_data()


func _on_load_pressed() -> void:
	var save_file = serializer.new()
	save_file.load_data()
	
	%player.global_position = save_file.player.player_position
class_name serializer
extends Resource

const SAVE_GAME_PATH = "user://save.tres"

@export var player : player_data = player_data.new()
@export var enemies : enemy_data = enemy_data.new()



func save_data():
	ResourceSaver.save(self, SAVE_GAME_PATH)
	
	
	
func load_data():
	var save_data = ResourceLoader.load(SAVE_GAME_PATH, "")
	player = save_data.player
	enemies = save_data.enemies
class_name player_data
extends Resource


@export var player_position : Vector2
class_name enemy_data
extends Resource

@export var enemy_position : Array[Vector2]

and yet when i enter the game and press save i get this error

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.

1 Like

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.

1 Like

Hi, how are you?

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.

1 Like

nvm, i fixed it for now

save.gd

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()