Help with save/load in platformer game

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ben2420

Hi, I’m making a simple 2D platform game, I’ve set up a main menu (with New Game and Continue buttons) and a pause screen while in game (with Save, Resume and Menu) buttons.

My problem is: I can get my ‘Save’ button to produce a .DAT file in the required user folder stating ‘World 1’ in text but I’m not sure how to get this to then load the game from this point when I press the ‘Continue’ button on the main menu. I want the game to continue when I press the ‘Continue’ button from the scene I was at when I saved the game in the pause menu.

This is my ‘main.gd’ where my save/load scripts are stored (I have this set to autoload in project settings):

    extends Node

var level = 1
var pause = false

func _ready():
	_load()

func _save():
	var data = ""
	data += "World_ " + str (level)
	var new_file = File.new()
	new_file.open("user://save.dat", File.WRITE)
	new_file.store_line(data)
	new_file.close()

func _load():
	var new_file = File.new()
	if not new_file.file_exists("user://save.dat"):
		_save()
		return
	new_file.open("user://save.dat", File.READ)
	var data = new_file.get_as_text()
	new_file.close()
	data = data.split("\n")
	for line in data:
		if line.begins_with("level"):
			level = int(line.split(" ")[1])

This is my MainMenu scene where my ‘New Game’ and ‘Continue’ buttons are:

 extends Control

var main = load("res://main.gd").new()

func _on_PlayButton_pressed():
	get_tree().change_scene("res://World_1.tscn")


func _on_ContinueButton_pressed():
	main._load()

I’ve been through all the guides and documentation and I can’t get any further… Help would be much appreciated as I’ve been really struggling to get it to work! Thanks!

:bust_in_silhouette: Reply From: Wakatta

Your code has some oddities like why are you saving in the _load function?
The line that causes your issue would be data += "World_ " + str (level) so that when you call if line.begins_with("level"): the line would actually be World_ 1

Not that looping your save file line by line is wrong but you’ll find better usability using a Dictionary with JSON. that being said i’ve took the leeway of adjusting your code as its not a true ‘dat’

var save_data = {
	"level": 1,
	"power": 0,
	"vitality": 6, 
	"dexterity": 40
}

func _save():
	var new_file = File.new()
	var error = new_file.open_encrypted_with_pass(
		"user://save.dat", File.WRITE, OS.get_unique_id()
	)
	assert(not error, "Could not create save file")
	new_file.store_line(to_json(save_data))
	new_file.close()

func _load():
	var new_file = File.new()
	
	var error = new_file.open_encrypted_with_pass(
		"res://save.dat", File.READ, OS.get_unique_id()
	)
	if error:
		print("Failed to load save file")
		return
	
	var data = parse_json(new_file.get_as_text())
	if typeof(data) != TYPE_DICTIONARY:
            print("Save data corrupt")
		return
		
	save_data = data
	file.close()

For testing purposes you can use new_file.open() but the final product should use new_file.open_encrypted_with_pass() additionally the benefit of using a Dictionary is to easily store and access your variables like save_data.level = 200

Thank you for your response, using this I’m now able to save as a real .DAT file in the project data folder.

My confusion is how do I save the game in a particular scene (“World_1”, “World_2”, “World_3” e.t.c) (with World_ being the names of my level scenes) and then when reloading the game upon pressing the ‘Continue’ button I’m returned to the scene where I saved at?
At the moment I save the game, it sends a file to the project data folder but this doesn’t mean I can continue from where I progressed to previously.

ben2420 | 2021-02-19 15:21

Depends on how your code is structured. Lets say your _save _load functions are in a Singleton which they should be

if your “Worlds” are being instanced by code you can do

func _ready():
    save_data.level = filename

func _on_ContinueButton_pressed():
    var level = load(save_data.level)
    replace_by(level.instance())

If your “Worlds” are Nodes in the tree that are just hidden / shown

func _ready():
    save_data.level = get_path()

func _on_ContinueButton_pressed():
    hide()
    get_node(save_data.level).show()

and in the Singleton call your save load functions like this:

func _notification(what):
	match what:
		NOTIFICATION_READY:
			_load()
		NOTIFICATION_WM_QUIT_REQUEST, NOTIFICATION_CRASH:
			_save()

Wakatta | 2021-02-19 15:57

Thank you for your help Wakatta. Honestly I’ve been trying with your help to get this working however I just can’t get the Continue button to load the game from where it was previously, when pressed it just does nothing all I can get it to do is print text to the console… It’s down to my limited coding knowledge as I’ve only been using Godot for a few weeks without previous experience, I think the only way I’ll be able to get it working for my game is through a screen share zoom call or sending my script on GitHub but I do appreciate your help and I’m definitely closer to getting it working :slight_smile:

ben2420 | 2021-02-19 19:02