Make Save System wait for Scene to be ready

Godot Version

4.4.1.

Question

`I made a save system trough a tutorial, sadly the tutorial didn’t show how to save and load the scene, added to the player position. A nice person helped me get the scene saving and loading to work, but that made the loading of the global position not work anymore.
I was told that it’s probably because everything get’s loaded before the scene is ready, and it can’t load the global position of a node that isn’t ready yet. I was also told there are mistakes in the rest of my code, and I tried to fix them a bit, but didn’t know how to do that.

So my question is, how would I make my save system wait, after loading the scene, and before loading the global position. I’m a complete noob so explaining it in a simple way would be very kind.
I added my save and load script here

func save():

print(get_path())
var file = FileAccess.open("user://savegame.json",FileAccess.WRITE)

var saved_data = {}
saved_data["scene_file_path"] = get_tree().get_current_scene().scene_file_path
saved_data["player_global_position:x"] = player.global_position.x
saved_data["player_global_position:y"] = player.global_position.y
saved_data["Zombie_Health_1"] = Globals.ZombieHealth1


var json = JSON.stringify(saved_data)

match FileAccess.get_open_error():
	OK:
		file.store_string(json)
		file.close()
		print("Save successful.")
	ERR_CANT_CREATE:
		print("Error: Cannot create the save file.")
	ERR_CANT_OPEN:
		print("Error: Cannot open the save file.")
	ERR_FILE_NOT_FOUND:
		print("Error: Save file not found, and could not be created.")
	_:
		print("An unknown error occurred while opening the save file.")

func load_game():
var file = FileAccess.open(“user://savegame.json”, FileAccess.READ)

match FileAccess.get_open_error():
	OK:
		var json = file.get_as_text()
		file.close()

		var saved_data = JSON.parse_string(json)
		

		if typeof(saved_data) == TYPE_DICTIONARY:
			
			if saved_data.has("scene_file_path"):
				get_tree().change_scene_to_file(saved_data["scene_file_path"])
				print ("got scene")
			else: 
				print ("Scene not found")
			
			if saved_data.has("player_global_position:x") and saved_data.has("player_global_position:y"):
				player.global_position.x = saved_data["player_global_position:x"]
				player.global_position.y = saved_data["player_global_position:y"]
				print("Loaded player position: ", player.global_position)
			else:
				print("Position data not found in save file.")

			if saved_data.has("Zombie_Health_1"):
				Globals.ZombieHealth1 = saved_data["Zombie_Health_1"]
				print("Loaded ZombieHealth1: ", Globals.ZombieHealth1)
			else:
				print("Zombie_Health_1 not found in save file.")
				
			
				
			
		else:
			print("Error parsing save file JSON.")
	
	ERR_FILE_NOT_FOUND:
		print("Save file not found.")
	_:
		print("Error opening save file: ", FileAccess.get_open_error())
		`

Every node has a ready signal that is emitted after the _ready() function is finished running and the node is done being constructed. I commonly run into this problem with my _physics_process() functions in CharacterBody2D and CharacterBody3D nodes. What happens is the physics process (and regular process) actually starts running before the ready signal is sent.

Here’s my solution:

var is_ready = false


func _ready() -> void:
	# Keep physics processing off until children are constructed, so that the
	# idle state doesn't try to call the animations before they exist.
	ready.connect(func(): is_ready = true)


func _physics_process(delta: float) -> void:
	if not is_ready:
		return
	# Do more stuff here.

Now I don’t know when you are calling save/load, but this might work for you. (This assumes that your load function is in the same file.) In your main level add this:

func _ready() -> void:
	ready.connect(load_game)
2 Likes

Hi! Thank you for your answere.
The save system is in a seperate file, that’s not connected to any of the scenes, but set as global so I can call save and load wherver I need it (I currently have it set to the Shift key for save and TAB key for load, while I’m trying things out)

Try this:

func _ready() -> void:
	ready.connect(_on_ready)

func _on_ready() -> void:
	AutoloadName.load_game()
1 Like

I sadly get an error when I try to start it, after adding that to my scene. it says "Invalid access to property or key ‘global_position’ on a base of a type ‘previously freed’

Ok, so what that means is you are running queue_free() on the object before loading a value into it. Don’t do that.

The strange thing is, I’m not running queue_free() on the player, or on the scene.
Could it be, that because the save system runs, and then runs a second time when the scene is ready, that it gets confused?

I tried dividing up the loading part into two functions, one where it loads the scene and one where it loads the global.position. It works on starting the scene, but when I press the key for loading it goes back to the problem of not loading the right position. Same for when I use the Continue button in my main menu, (which is in a different scene)

You should only be running it when the object is ready.

Well that’s information to help you debug. Something is different about either the code being run, or the way the code is being run. Find it and you’ll find your problem.