Trying to store the player's current position for a 'save system' I am creating

Godot Version

4.2.1

Question

Hello again. I am creating a ‘save system’ for my game, I have followed a tutorial on Youtube, but I can’t figure out how to save the player’s current position in the JSON save file that it creates, I have this ‘GameData’ Script that extends Resource which contains the var Vector2, I am not sure how to assign the player’s current position from the player script to the var Vector2 that is within the GameData script. How I am my supposed to do this?

Save System script, I have this as an autoload

extends Node

const SAVE_DIR = "user://saves/"
const SAVE_FILE_NAME = "save.json"

var game_data = GameData.new()


func _ready():
	verify_save_directory(SAVE_DIR)


func verify_save_directory(path : String):
	DirAccess.make_dir_absolute(path)


func save_data(path : String):
	var file = FileAccess.open(path, FileAccess.WRITE)
	if file == null:
		print(FileAccess.get_open_error())
		return
	
	var data = {
		"game_data":{
			"player_global_position":{
				"x": game_data.player_global_position.x,
				"y": game_data.player_global_position.y 
			}
		}
	}
	
	var json_string = JSON.stringify(data, "\t")
	file.store_string(json_string)
	file.close()
	


func load_data(path : String):
	if FileAccess.file_exists(path):
		var file = FileAccess.open(path, FileAccess.READ)
		if file == null:
			print(FileAccess.get_open_error())
			return
			
		var content = file.get_as_text()
		file.close()
		
		var data = JSON.parse_string(content)
		if data == null:
			printerr("Cannot parse %s as a json_string: (%s)" % [path, content])
			return
		
		game_data = GameData.new()
		game_data.player_global_position = Vector2(data.game_data.player_global_position.x, data.game_data.player_global_position.y)
		
	else:
		printerr("Cannot open non-existent file at %s!" % [path])

GameData Script

extends Resource
class_name GameData

@export var player_global_position = Vector2.ZERO

You need a player.
Do you have a player?

Then, when saving you have to use player.global_position not player_global_position.

When loading, you have to actually use the loaded data to position the player.

player.global_position = game_data.player_global_position

I have a player in the scene

Sorry if I have no idea what I am doing

Is no one going to help?

Bro, how does game data, a resource, have the players position?

Resources are unique. If player_global_position isn’t a static variable that the player updates with their global position, it won’t get your players position of you create a new instance. I would suggest that you have an way for the player to reach the autoload and set an player position variable in the autoload, or use static variables as I mentioned above.

That’s what I don’t know, it dosen’t seem to have access to the player’s global_position, from what I can tell in remote tab during run time

So are you doing anything to put the player position in the variable, or are you just hoping that naming the variable makes it work. I told you some ways to put the players position in the variable above. Static vars are constructed like this

static var player_global_position:Vector2= Vector2.ZERO

And now, you can access them from anywhere using

GameData.player_global_position=get_global_position()

without having to instantiate it. Even better, you can access this from any instance or non instance and it will be whatever value you put in it last, no matter what or where you’re instancing it from.

1 Like

Okay. Heres what I did in the player script

func _physics_process(_delta: float):
	GameData.player_global_position = get_global_position()
	currentPosition = global_position
	handle_input()
	move_and_slide()
	update_animation()

When I press the save input or the save button in the pause menu, i does store the values now. But when I press load nothing happens, it dosen’t place the player in the saved position, what do I need to do inside the load_data function?

What the save file looks like

{
	"game_data": {
		"player_global_position": {
			"x": 940.283508300781,
			"y": 545.116088867188
		}
	}
}

I will repeat the same answer:

When loading, you have to actually use the loaded data to set the position in the player script. For example:

func _ready():
  global_position = game_data.player_global_position

So, you need to have some way to take the player global position in the ready function if it is not vector (0,0) (unless (0,0) is the players default spawning point at the beginning of the game.). Basically Timothy’s answer but with an if condition that sets your player to it’s spawning point if the saved position is (0,0) which means the player has not moved. When do you call the load data function? It should preferably be in _init so that you can set all the save data up accordingly when the game starts.

Sorry if I am not getting you. I am still new to Gdscript:

This player.global_position line you are talking about, where am I supposed to put it? In the SaveSystem script?

This where I put it?

func save_data(path : String):
	var file = FileAccess.open(path, FileAccess.WRITE)
	if file == null:
		print(FileAccess.get_open_error())
		return
	
	var data = {
		"game_data":{
			"player_global_position":{
				"x": GameData.player_global_position.x, # Here?
				"y": GameData.player_global_position.y # Here?
			}
		}
	}
	
	var json_string = JSON.stringify(data, "\t")
	file.store_string(json_string)
	#file.store_var(player_position)
	file.close()
	

Or here?

func load_data(path : String):
	if FileAccess.file_exists(path):
		var file = FileAccess.open(path, FileAccess.READ)
		if file == null:
			print(FileAccess.get_open_error())
			return
			
		var content = file.get_as_text()
		file.close()
		
		var data = JSON.parse_string(content)
		if data == null:
			printerr("Cannot parse %s as a json_string: (%s)" % [path, content])
			return
		
		GameData.player_global_position = Vector2(GameData.player_global_position.x, # Here?
		GameData.player_global_position.y) # Here?
		
	else:
		printerr("Cannot open non-existent file at %s!" % [path])

And if I put this in the player _ready() function, when the game starts the player spawns in the top left corner of the map out of bounds and can’t move

func _ready():
  global_position = game_data.player_global_position

I said to check if the position in game data is vector2.zero, didn’t I though

0,0 is the top left, and this can be avoided if you check that the position is 0,0 or not. If it isn’t, that means there is savedata, so use the save data. Else use the normal spawning position.

I have made the variable static in Gamedata script like you said

extends Resource
class_name GameData


static var player_global_position:Vector2 = Vector2.ZERO

Also, it would help if you told us where you call the load data function.

I didn’t even say anything about that. That is fine. I said to check if the position in the game data resource is 0,0

I am calling it from the these Input events in the SaveSystem script

func _input(event):
	if event.is_action_pressed("save"):
		save_data(SAVE_DIR + SAVE_FILE_NAME)
		print_debug("Saved.")
	if event.is_action_pressed("load"):
		load_data(SAVE_DIR + SAVE_FILE_NAME)
		print_debug("Loaded.")

Or in the pause_menu script

extends Control

signal opened
signal closed

var isOpen: bool = false


func open():
	visible = true
	isOpen = true
	opened.emit()


func close():
	visible = false
	isOpen = false
	closed.emit()


func _on_continue_button_pressed():
	close()


func _on_quit_button_pressed():
	get_tree().quit()

# Here
func _on_save_button_pressed():
	SaveSystem.save_data(SaveSystem.SAVE_DIR + SaveSystem.SAVE_FILE_NAME)


func _on_load_button_pressed():
	SaveSystem.load_data(SaveSystem.SAVE_DIR + SaveSystem.SAVE_FILE_NAME)

Then you need to make sure that the player knows when the load function is called. Maybe make a signal in the autoload that the player connects to in ready, and the function it connects to makes the players position be the position in the game data. You have to make sure to emit the signal after you load the position in the game data position or else it will load to 0,0.