Setting up a global for using variables across scenes

Hi there. Wanted to make sure I got this right before I did it. I’m wanting to persist my button toggle and variables across scenes using a global script. It seems for this current script it would mean cutting and pasting the lines prior to the ready function to the global script and then prepending the variable uses with the global script name? I’m not sure about the ready function as I may use the data differently in other menus so probably won’t be necessary to include in the global. Basically I want the choices in all 4 sub menus to persist while going between them. I may also display the results of each choice made in the sub menus to the main menu but haven’t decided yet. I’ve browsed a few of the options for persistent data and a global script seems to be the best option as I don’t need anything to persist outside of the session.

extends Control

var enviroatlas = preload("res://EnviroAtlas.tres")
@export var enviro = preload("res://enviro.tres")
var envdict = {"Aeternus":[0, 0, 5], "Diamond":[300, 0, 4], "Freedom":[600, 0, 1], "Insula":[900, 0, 8],
"Magmaria":[0, 200, 4], "Megalopolis":[300, 200, 2], "Pike":[600, 200, 8], "Rook":[900, 200, 8],
"Silver":[0, 400, 4], "Block":[300, 400, 6], "Wasteland":[600, 400, 7], "Discord":[900, 400, 5],
"Atlantis":[0, 600, 7], "ZhuLong":[300, 600, 6], "Anubis":[600, 600, 7], "Wagner":[900, 600, 6]}

func _ready() -> void:
	for n in enviro.get_buttons():
		n.toggled.connect(_enviropressed)
	enviroatlas.region = Rect2(envdict[enviro.get_pressed_button().name][0], envdict[enviro.get_pressed_button().name][1], 300, 200)
	$choice.texture = enviroatlas
	$Envdiff.text = "Difficulty " + str(envdict[enviro.get_pressed_button().name][2])

func _enviropressed(toggled_on: bool) -> void:
	if toggled_on:
		enviroatlas.region = Rect2(envdict[enviro.get_pressed_button().name][0], envdict[enviro.get_pressed_button().name][1], 300, 200)
		$choice.texture = enviroatlas
		$Envdiff.text = "Difficulty " + str(envdict[enviro.get_pressed_button().name][2])



func _on_button_17_pressed() -> void:
	get_tree().change_scene_to_file.call_deferred("res://Mainmenu.tscn")


func _on_button_pressed() -> void:
	var envkey = envdict.keys()
	var renv = envkey.pick_random()
	for n in enviro.get_buttons():
		if n.name == renv:
			n.button_pressed = true

Yes. Godot basically creates a node with the autoload script attached before your other scenes gets ready.

This doesn’t seem to be working. Changing scenes doesn’t keep the variables. As far as I can tell it’s due to loading the button group resource each time. I can’t see a way around this so may have to switch to saving using fileaccess and a conditional that checks for it in the ready function. Does fileaccess overwrite or can I append extra variables as they’re created?

You need to use an Autoload for global data. Alternately, you can persist the data on disk. Check out my Disk Plugin, specifically the save_setting() and load_setting() functionality. I think you will find it well documented and easier to use than what you’ve got going on for persisting data.

This is your problem. Each Control is loading its own version of this file. So changing the data to one, doesn’t change it for the others.

You’re using magic strings here. $choice and $Envdiff should be @onready variables. You can drag and drop (press Ctrl when dropping) the nodes into your code, and @onready variables will be created for you.

Depends on how you’re storing your data and what your read/write codes looks like. If you use my Disk Plugin, it’ll handle things like that for you. If you want to do it for yourself, you can learn from the code.

Thanks for confirming it’s the preload. I don’t have any file saves yet and will check out your code as at the very least it will help me with the syntax. @onready using ctrl could be an issue using the mobile version but will look at that also. There’ll only be 8 - 12 variables to track but from across 4 menus. I was thinking about using files at a later date to record the session histories.

What about using resource saver to overwrite enviro.tres or copy it to the user dir? The only data I need to save from this script is which button in the group is toggled and that currently is the only thing that changes for that buttongroup. I assume that saving the resource after changing the toggled button should keep the change.

You actually don’t need autoloads for global persistent data. Any static variable in a named script will be global and persistent.

1 Like

Ok. So regarding static variables in this case it would be best to declare them in the script for the main menu, which is accessed first, rather than this script for a sub menu? Which would be the var enviro but I still need to load the resource in. To avoid issues with loading the resources and static variables would it be even better to use a loading screen prior to the main menu to set up the variables and exit straight out into the main menu? If I add this scene, can I make it the root scene or will I have to start from scratch?

If a static variable is closely related to a script - you can declare it in that script. If it’s only accessed from that script you don’t even need to name the script class.

If you want to closely emulate what you’d get with an autoloaded node, make a dedicated named “static class” that holds the data. It can extend any type, it doesn’t matter. Fill it with your static var declarations and name it Global. Now you can access those vars from anywhere just doing Global.some_variable

1 Like

It’s going to be accessed in multiple scripts, one of which will need to preload the resource it uses. The other variables when I create them will also be buttongroup resources which will need to be loaded at some point. You didn’t confirm what I need to know which is as it currently stands, making the variable static won’t help as switching to the script provided will just overwrite it with the resource when it’s preloaded again.

To make the persistence work using static variables, am I going to need to create a new root scene to preload the resources in that does not get entered again? I can then call the static variables in all the other scenes that I can enter multiple times with the data persisting at its current value.

I don’t get what you mean. A static var is functionally exactly the same as a variable in an autoloaded node. You just don’t get _ready() to initialize it, but you can use _static_init() or initialize at declaration or from somewhere else.

What I mean is that if the var enviro needs to be static for persistence, every time I enter the scene with the current script it will preload the buttongroup into the variable which will essentially set it back to the default value. So it seems I should preload the resources in a one use scene so the preload only happens once.

preload() happens at compile time and results in a constant.

Static var assignment that’s specified at its declaration line happens only once on startup.

So changing a scene won’t affect values of static vars in any way unless you run some code that explicitly does so.

Specifically:

static var enviro = preload("res://enviro.tres")

will run only on startup. Loading a scene with a node that has this script attached won’t re-run this initialization.

The current preload explicitly changes the static. Thats what I’ve been saying.

Post the actual script. Or better yet a minimal reproduction example that demonstrates this.

Here’s a simple example to try that demonstrates the persistence:

extends Node

static var foo := 0

func _ready():
	print(foo)
	foo += 1

func _input(e):
	if e.is_action_pressed("ui_accept"):
		get_tree().reload_current_scene()

As you repeatedly press enter the ouptut is:

0
1
2
3
4
5

So static var foo := 0 runs once at startup and never again after that, no matter what you do with the scene/script.

Static variables are not related to objects i.e. class instances. They live on their own forever-cloud, and just use the namespace of the class they’re declared in to make themselves globally accessible.

1 Like

I don’t think you’re getting what I’m saying. The variable that needs to persist is the buttongroup resource that I’m preloading. The only script I have at the moment is the one I’ve already posted because I want the answer to my problem before I code anything else. I’ve already created a global script and had to delete it because that solution didn’t work due to the preload occurring each time.

What do you mean by “each time”? You mean each time you press the play button in the editor? You want changes to persist between run sessions? In that case you’ll have to re-save the resource if the script does some changes to it at runtime.

As previously stated each time the scene is changed to.

As I said, preload() that’s assigned to a static var won’t re-execute when the scene is changed. You may be doing something else wrong.

Post your actual script that shows the problem.