Godot Version
4.7
Question
I was following this great tutorial on how to create a save system by Queble (https://www.youtube.com/watch?v=OXp25cZDpEw) and it worked beautifully, but now I’m having issues making sure it connects and works with my game.
I’m having trouble figuring out how to access the load and save functions from outside the map (for menus and special maps that needs different scripts) and have it be all just 1 save and not per level like he has it in his video.
I want for my game for the save to just update per level adding the new info and saving which level the person is on. All the properties saving is working perfectly, I just need help with making sure it’s all just 1 save, saving where the player is and having access to it on my global scripts for the menus and maps.
Currently I have the save scripts as Queble and it is working just like his I just need help with these small changes and connections.
This is the Saver_node script:
@tool
class_name SaverNode extends Node
const SAVER_NODE_GROUP : String = "saver_node"
@export var properties_to_save: Array[String] = []
var suggested_properties : Array[String] = []
func _ready() -> void :
add_to_group(SAVER_NODE_GROUP)
if Engine.is_editor_hint():
_update_property_list()
func _update_property_list()-> void:
var parent = get_parent()
if not parent:
return
suggested_properties.clear()
var all_props = parent.get_property_list()
for p in all_props:
var pname = p.name
suggested_properties.append(pname)
notify_property_list_changed()
func _validate_property(property: Dictionary)-> void:
if property.name == "properties_to_save":
var options = ",".join(suggested_properties)
property.hint = PROPERTY_HINT_TYPE_STRING
property.hint_string = "%d/%d:%s" % [TYPE_STRING, PROPERTY_HINT_ENUM, options]
func get_save_dict()-> Dictionary:
var parent = get_parent()
var node_data = {}
for prop in properties_to_save:
print("Saving property:", prop)
if prop in parent:
node_data[prop] = parent.get(prop)
return node_data
func apply_save_dict(node_data: Dictionary)-> void:
var parent = get_parent()
for prop in node_data:
if prop in parent:
parent.set(prop, node_data[prop])
This is the Level_save_data script:
class_name LevelSaveData extends Resource
@export var data: Dictionary = {}
This is my base_level script:
class_name Level extends Node2D
const LEVEL_SAVES_FOLDER : String = "user://level_saves/"
enum LevelIDs{
NONE = 0,
LEVEL_1 = 1,
LEVEL_2 = 2,
LEVEL_3 = 3,
OFFICE_LEVEL_1 = 4,
LEVEL_4 = 5,
LEVEL_5 = 6,
LEVEL_6 = 7,
LORE_LEVEL = 8,
LEVEL_7 = 9,
LEVEL_8 = 10,
FINAL_LEVEL = 11,
}
@export var level_id : LevelIDs
func _ready() -> void:
GlobalAudioManager.base_level_music()
DirAccess.make_dir_absolute(LEVEL_SAVES_FOLDER)
load_level_data()
save_level_data()
if SceneManager.spawn_door_tag != null:
_on_level_spawn(SceneManager.spawn_door_tag)
func get_level_save_file_path()-> String:
return LEVEL_SAVES_FOLDER + str("level_save_", level_id) + ".tres"
func save_level_data()-> void:
var new_save = LevelSaveData.new()
for saver: SaverNode in get_tree().get_nodes_in_group(SaverNode.SAVER_NODE_GROUP):
var node_path = get_path_to(saver)
new_save.data[node_path] = saver.get_save_dict()
ResourceSaver.save(new_save, get_level_save_file_path())
print("Saved Level data to",get_level_save_file_path())
func load_level_data()-> void:
if not FileAccess.file_exists(get_level_save_file_path()):
return
print("loading map data from", get_level_save_file_path())
var loaded_save = ResourceLoader.load(get_level_save_file_path()) as LevelSaveData
for path in loaded_save.data:
var saver_node = get_node_or_null(path)
if saver_node:
saver_node.apply_save_dict(loaded_save.data[path])
func _on_level_spawn(destination_tag: String):
var door_path = "Door_" + destination_tag
var door = get_node(door_path) as Door
SceneManager.trigger_player_spawn(door.spawn.global_position, door.spawn_direction)
This is the game manager that controls the menus (cannot access load and save from here):
extends Node
var main_menu = preload("res://UI/MainMenu.tscn")
var pause_menu = preload("res://UI/PauseMenu.tscn")
# Called when the node enters the scene tree for the first time.
func _ready():
RenderingServer.set_default_clear_color(Color(0,0,0,1))
func _toggle_fullscreen(toggle):
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_WINDOWED:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
func start_game():
if get_tree().paused:
continue_game()
return
SceneManager.transiton_to_scene("Intro Cut Scene", "0")
func exit_game():
get_tree().quit()
func pause_game():
get_tree().paused = true
var pause_menu_instance = pause_menu.instantiate()
get_tree().get_root().add_child(pause_menu_instance)
func continue_game():
get_tree().paused = false
func backtomain_menu():
var main_menu_instance = main_menu.instantiate()
get_tree().get_root().add_child(main_menu_instance)
And this is my scene manager that controls the changing scenes (cannot access load and save from here):
extends Node
var scene_transition_screen = preload("res://UI/screen_transition/scene_transition_screen.tscn")
var scenes : Dictionary = {"Level 1": "res://Levels/level_1.tscn",
"Level 2": "res://Levels/level_2.tscn",
"Level 3": "res://Levels/level_3.tscn",
"Level 4": "res://Levels/level_4.tscn",
"Level 5": "res://Levels/level_5.tscn",
"Level 6": "res://Levels/level_6.tscn",
"Level 7": "res://Levels/level_7.tscn",
"Level 8": "res://Levels/level_8.tscn",
"Final Level": "res://Levels/Final_level.tscn",
"Lore Level": "res://Levels/lorelevel.tscn",
"office Level 1":"res://Levels/office_level_1tscn.tscn",
"Credits":"res://UI/credits.tscn",
"Final Cut Scene": "",
"Intro Cut Scene": "res://UI/CutScenes/beginning_cut_scene.tscn"
}
var level : String
signal on_trigger_player_spawn
var spawn_door_tag
func transiton_to_scene(level : String, destination_tag):
var scene_path : String = scenes.get(level)
if scene_path != null:
var scene_transition_screen_instance = scene_transition_screen.instantiate()
spawn_door_tag = destination_tag
get_tree().get_root().add_child(scene_transition_screen_instance)
await get_tree().create_timer(2.5).timeout
get_tree().change_scene_to_file(scene_path)
scene_transition_screen_instance.queue_free()
func trigger_player_spawn(position: Vector2, direction: String):
on_trigger_player_spawn.emit(position,direction)
# Called when the node enters the scene tree for the first time.
func _ready():
pass






