Random beginner-question: How to refresh info out of the save-file

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

Hi everyone,

my saving-system works! When hitting the save-button, things are being correctly saved in my save.txt-file (e.g. … ,“mode_off”:false, … (or true respectively).
Alas, hitting the load-button does not consider these actual entries. What happens is:

  • either nothing (although “loading” is being printed!) or:
  • if I add “get_tree().reload_current_scene()” on my “load_button_pressed”-function, the original state of the game is being refreshed, like I “left the code at initial start”. So if I manually change things to true or false within the code (e.g. “mode_off = true”) and then load the scene, things will be shown accordingly and jump back to that state at load-button-press, but not as they are written in the save.txt-file.

How could I have these “true/false” parts in the code be replaced according to the save.txt-entries when pressing the load-button, so “reload_current_scene” could consider the “corrected” code?
What I’d like (and assume) is something replacing “true” within the code with “getting the currently saved word”:
Instead of
“mode_off = true”
something working as
“mode_off = get-that-according-word-from-within-the-save.txt-file-right-here-in-place”

Something in my mind tells me that this could be achieved fairly easily, but I’m still too much beginner to pull it off…
I’m asking about such a way because I tried everything else I could think of, mind you! Like getting the variable over with a setget approach or get_node(“XYZ”).var_name = variable and var var_name = get_node(“…/NodePath”).active etc., some export-trials and what not, but all to no avail.
Any suggestions would be much appreciated!

:bust_in_silhouette: Reply From: max.marauder

Hey!
I’d suggest you to check out Singletons (AutoLoad):

You can define an AutoLoad scene, e.g. State, which would have all the variables that you need in your save files, as well as save() and load() methods that save all those variables into a file and load them back from file.
Then in any other part of your game (e.g. in some scene’s _ready() method) you can use those variables e.g.:

func _ready():
   mode_off = State.mode_off

This way you can limit responsibility for saving and loading the game (calling State.save() and State.load() to one specific place (e.g. your game’s main scene), and the rest of the scenes will just use variables from State as much as they need to.
I hope this is somewhere close to answering your question :slight_smile:

Hi max.marauder, thanks for answering!

For some reason one or two basic “sequence-structure-mechanic” things just got clear reading your answer, at least I’d like to think so:

  • any scene anywhere could grab it’s (current) variables “from the State” in the main scene by State.something… what I mean is: that variable would easily be “found” in the main scene’s “State” just by “mentioning” State in the _ready function, is that right? (It seems like I just circle back to some ultimate beginner thoughts, fittingly…) The “path” to the info is the sticking point here - it wouldn’t be explicitly necessary, right? The scene would just find all it needs directly in “State” and it would “know where to look” for it?

  • and the “State” in the main scene would serve like some go-between, gathering all info and preparing all the fresh variables for any scene that needs some, right from the start, auto-loaded before anything else. So that would mean that the current states are being “refreshed” at startup… but also while running? I’ll try and see.

It all sounds logical and “reasonable”, now I just have to succeed in building it.

Thanks again!

pferft | 2020-10-15 16:14

:bust_in_silhouette: Reply From: Bernard Cloutier

It would be simpler if you posted your code, instead of describing the code. Often, the error lies in parts we overlook when trying to describe it. (Also it’s easier to understand code by reading it instead of being told about it)

Have you done this tutorial on saving and loading? Saving games — Godot Engine (stable) documentation in English

If so, is your save/load mechanism similar? If not, why and how is it different?

It sound like your save file contains a list of node paths and their properties, and that when loading you want to get those properties from the file into their corresponding in-game node. If that’s all you need, then the tutorial should cover it. If not, feel free to ask me for more help (although if you do, please post your save/load scripts!).

Hi Bernard, thanks for the hint and for offering help. I’ll get back to you as soon as I can, and with some code then : )

pferft | 2020-10-15 16:26

Alright, I’ll try to show my setup (cutting out all button-animation related code etc.).
As I say, opening the save.txt-file shows me that saving works – the variables are being altered there accordingly. Pressing the load-button also prints “loading”, but it just won’t refresh exactly what it is loading in the scene itself.

My “game” (for mobile-device – everything meant for touch-input) consists of nothing but a little colored icon at the top of the screen and three checkboxes below with which I can change the color of that icon (blue, green and red). At the bottom are a save- and a load-button. That’s it.

This is what my scene looks like:

My Base-Node is “TheOrigin”:

extends Node2D

onready var save_load: SaveLoadClass = $SaveLoadNode

func _on_ButtonSave_save_button_was_just_released():
	save_load.save_states()

func _on_ButtonLoad_load_button_was_just_released():
	save_load.load_states()
	get_tree().reload_current_scene()
# at least it's doing something, if yet not what it should...
# it reloads the scene but not considering the saved values.

The “first child” is my SaveLoadNode, basically just a script which I also put to AutoLoad:

extends Node

class_name SaveLoadClass

func save_states():
	print("Saving...")
	var save_game = File.new()
	save_game.open("res://save.txt", File.WRITE) # later then "user", I know...

	var save_nodes = get_tree().get_nodes_in_group("ToSave")
	for save_node in save_nodes:
		var save_dict = {
			"name": save_node.name,
			"value": save_node.pressed, # yes, "value" and "on" are the same here (just a test...)
			"path": save_node.get_path(),
			"on": save_node.pressed
		}
		# anti-confusion note to self: alphabetical order in the save-file
		save_game.store_line(to_json(save_dict))

	save_game.close()


func load_states():
	print("Loading...")
	var save_game = File.new()
	if not save_game.file_exists("res://save.txt"):
		print("No save game exists")
		return
	
	save_game.open("res://save.txt", File.READ)
	while save_game.get_position() < save_game.get_len():
	
		var save_data = parse_json(save_game.get_line())
		var node_to_change = get_node(save_data["path"])
		node_to_change.pressed = save_data["value"] # again, "value" and "on" are the same here...
		node_to_change.pressed = save_data["on"]
		
	save_game.close()

The other child of TheOrigin is TheFirstNode with its children CheckBox1Base, CheckBox2Base and CheckBox3Base, each member of the group ToSave and with this script (accordingly, excerpt is from CheckBox1Base (blue)):

extends Node2D

var pressed = false

var mode_on = false
var mode_off = false


func _ready():
	mode_off = true
	# mode_on = true # here deactivated, so the checkbox is "off" at loadup.
# This is loaded at load-button-press! But it's not being replaced by the latest saved value.
# Shouldn't it work if this state would be updated at startup/load-button-press?
# How can I have this updated/replaced with the latest saved value? That's the big question.

	if mode_on == true:
		get_node("CheckBox1AnimPlayer").play("just-big-full")
	if mode_off == true:
		get_node("CheckBox1AnimPlayer").play("just-big-empty")

########################################################################

	var get_signal_from_checkbox_2
	get_signal_from_checkbox_2 = get_tree().get_root().find_node("CheckBox2Base", true, false)
	get_signal_from_checkbox_2.connect("checkbox_2_on", self, "checkbox_2_on_received")
	get_signal_from_checkbox_2.connect("checkbox_2_off", self, "checkbox_2_off_received")

	var get_signal_from_checkbox_3
# same like checkbox_2...

func checkbox_2_on_received():
	# animate to "off"...
	pressed = false
func checkbox_2_off_received():
	print("CheckBox 2 OFF (I'm 1)")
	# animated accordingly...

func checkbox_3_on_received(): # just like _2_...

########################################################################

func _on_CheckBox1Area_input_event(_viewport, _event, _shape_idx):
# all kind of animation-stuff and then:

	if Input.is_action_just_released("mousebuttonclick"):
		if mode_off == true or mode_on == false:
			pressed = true
# here a tween manages the color of my icon...

			mode_off = false
			mode_on = true
			return

		if mode_on == true:
			pressed = false

			mode_on = false
			mode_off = true

The final children of TheFirstNode are ButtonSave and ButtonLoad, basically just releasing their signals:

if Input.is_action_just_released("mousebuttonclick"):
	emit_signal("save_button_was_just_released")

or “load_button was_…”

(These are all self-made buttons (CollisionShape2D and all…).)

I tried the tutorials, had them working, but I just can’t make my scene being refreshed at load-button-press. Any hint would be much appreciated.
(I’ll also happily upload a whole example-project, if you’d like!)

pferft | 2020-10-19 13:29

I haven’t read your whole reply, but I noticed this:

func _on_ButtonLoad_load_button_was_just_released():
    save_load.load_states()
    get_tree().reload_current_scene()

Now assuming your load_states method works, why would you call reload_current_scene() after loading from the file? Of course you’re going to end up with your scene in its initial state!

Bernard Cloutier | 2020-10-19 15:51

Ah, that is a yet rather desperate try to “refresh” the scene after loading, as it wouldn’t like it should yet. The idea is to load the saved variables “into position” and then refresh the scene to have them tatking effect. Didn’t work though…

I believe, as max.marauder suggests, if I could get the currently save variables from my SaveLoadNode into my CheckBox-Nodes, like:
func _ready():
mode_off = SaveLoadNode.mode_off
that would do the trick. I just can’t figure out how to “prepare” this mode_off in the SaveLoadNode in order to make it “grabable” from somewhere else then.

pferft | 2020-10-19 15:57

What I meant is what if you switch the statements around? First reload the scene, then load the state.

Bernard Cloutier | 2020-10-19 16:36

It doesn’t seem to change anything.
But I really believe that my code just isn’t complete yet for any effect to take place in this. I can’t figure out how to call the actual value into the func _ready(): of my checkboxes yet. They are in the save-file, but I can’t get them out.

EDIT:

I managed to have the saved stated being printed to the Output at load-button-press, so it’s definitely there. The only thing missing is refreshing…
Don’t want to seem overzealous, but I uploaded the current project just in case you’d like to take a look. Maybe the answer to the problem is plainly obivous that way? (I’d like to hope so! ; )
WeTransfer - Send Large Files & Share Photos Online - Up to 2GB Free

I never thought saving/loading could be such an endeavour!

pferft | 2020-10-19 16:48