|
@@ -6,7 +6,7 @@ Saving games
|
|
|
Introduction
|
|
|
------------
|
|
|
|
|
|
-Save games can be complicated. For example, it may be desirable
|
|
|
+Save games can be complicated. For example, it may be desirable
|
|
|
to store information from multiple objects across multiple levels.
|
|
|
Advanced save game systems should allow for additional information about
|
|
|
an arbitrary number of objects. This will allow the save function to
|
|
@@ -90,9 +90,9 @@ like this:
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
- public Dictionary<object, object> Save()
|
|
|
+ public Godot.Collections.Dictionary<string, object> Save()
|
|
|
{
|
|
|
- return new Dictionary<object, object>()
|
|
|
+ return new Godot.Collections.Dictionary<string, object>()
|
|
|
{
|
|
|
{ "Filename", GetFilename() },
|
|
|
{ "Parent", GetParent().GetPath() },
|
|
@@ -123,7 +123,7 @@ loading.
|
|
|
Saving and reading data
|
|
|
-----------------------
|
|
|
|
|
|
-As covered in the :ref:`doc_filesystem` tutorial, we'll need to open a file
|
|
|
+As covered in the :ref:`doc_filesystem` tutorial, we'll need to open a file
|
|
|
so we can write to it or read from it. Now that we have a way to
|
|
|
call our groups and get their relevant data, let's use :ref:`to_json()
|
|
|
<class_@GDScript_method_to_json>` to
|
|
@@ -134,9 +134,9 @@ way to pull the data out of the file as well.
|
|
|
.. tabs::
|
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
|
|
- # Note: This can be called from anywhere inside the tree. This function is
|
|
|
+ # Note: This can be called from anywhere inside the tree. This function is
|
|
|
# path independent.
|
|
|
- # Go through everything in the persist category and ask them to return a
|
|
|
+ # Go through everything in the persist category and ask them to return a
|
|
|
# dict of relevant variables
|
|
|
func save_game():
|
|
|
var save_game = File.new()
|
|
@@ -147,24 +147,24 @@ way to pull the data out of the file as well.
|
|
|
if node.filename.empty():
|
|
|
print("persistent node '%s' is not an instanced scene, skipped" % node.name)
|
|
|
continue
|
|
|
-
|
|
|
+
|
|
|
# Check the node has a save function
|
|
|
if !node.has_method("save"):
|
|
|
print("persistent node '%s' is missing a save() function, skipped" % node.name)
|
|
|
continue
|
|
|
-
|
|
|
+
|
|
|
# Call the node's save function
|
|
|
var node_data = node.call("save")
|
|
|
-
|
|
|
+
|
|
|
# Store the save dictionary as a new line in the save file
|
|
|
save_game.store_line(to_json(node_data))
|
|
|
save_game.close()
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
- // Note: This can be called from anywhere inside the tree. This function is
|
|
|
+ // Note: This can be called from anywhere inside the tree. This function is
|
|
|
// path independent.
|
|
|
- // Go through everything in the persist category and ask them to return a
|
|
|
+ // Go through everything in the persist category and ask them to return a
|
|
|
// dict of relevant variables
|
|
|
public void SaveGame()
|
|
|
{
|
|
@@ -180,17 +180,17 @@ way to pull the data out of the file as well.
|
|
|
GD.Print(String.Format("persistent node '{0}' is not an instanced scene, skipped", saveNode.Name));
|
|
|
continue;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Check the node has a save function
|
|
|
if (!saveNode.HasMethod("Save"))
|
|
|
{
|
|
|
GD.Print(String.Format("persistent node '{0}' is missing a Save() function, skipped", saveNode.Name));
|
|
|
continue;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Call the node's save function
|
|
|
var nodeData = saveNode.Call("Save");
|
|
|
-
|
|
|
+
|
|
|
// Store the save dictionary as a new line in the save file
|
|
|
saveGame.StoreLine(JSON.Print(nodeData));
|
|
|
}
|
|
@@ -208,33 +208,33 @@ load function:
|
|
|
.. tabs::
|
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
|
|
- # Note: This can be called from anywhere inside the tree. This function
|
|
|
+ # Note: This can be called from anywhere inside the tree. This function
|
|
|
# is path independent.
|
|
|
func load_game():
|
|
|
var save_game = File.new()
|
|
|
if not save_game.file_exists("user://savegame.save"):
|
|
|
return # Error! We don't have a save to load.
|
|
|
|
|
|
- # We need to revert the game state so we're not cloning objects
|
|
|
- # during loading. This will vary wildly depending on the needs of a
|
|
|
+ # We need to revert the game state so we're not cloning objects
|
|
|
+ # during loading. This will vary wildly depending on the needs of a
|
|
|
# project, so take care with this step.
|
|
|
# For our example, we will accomplish this by deleting saveable objects.
|
|
|
var save_nodes = get_tree().get_nodes_in_group("Persist")
|
|
|
for i in save_nodes:
|
|
|
i.queue_free()
|
|
|
|
|
|
- # Load the file line by line and process that dictionary to restore
|
|
|
+ # Load the file line by line and process that dictionary to restore
|
|
|
# the object it represents.
|
|
|
save_game.open("user://savegame.save", File.READ)
|
|
|
while save_game.get_position() < save_game.get_len():
|
|
|
# Get the saved dictionary from the next line in the save file
|
|
|
var node_data = parse_json(save_game.get_line())
|
|
|
-
|
|
|
+
|
|
|
# Firstly, we need to create the object and add it to the tree and set its position.
|
|
|
var new_object = load(node_data["filename"]).instance()
|
|
|
get_node(node_data["parent"]).add_child(new_object)
|
|
|
new_object.position = Vector2(node_data["pos_x"], node_data["pos_y"])
|
|
|
-
|
|
|
+
|
|
|
# Now we set the remaining variables.
|
|
|
for i in node_data.keys():
|
|
|
if i == "filename" or i == "parent" or i == "pos_x" or i == "pos_y":
|
|
@@ -244,7 +244,7 @@ load function:
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
- // Note: This can be called from anywhere inside the tree. This function is
|
|
|
+ // Note: This can be called from anywhere inside the tree. This function is
|
|
|
// path independent.
|
|
|
public void LoadGame()
|
|
|
{
|
|
@@ -252,22 +252,22 @@ load function:
|
|
|
if (!saveGame.FileExists("user://savegame.save"))
|
|
|
return; // Error! We don't have a save to load.
|
|
|
|
|
|
- // We need to revert the game state so we're not cloning objects during loading.
|
|
|
- // This will vary wildly depending on the needs of a project, so take care with
|
|
|
+ // We need to revert the game state so we're not cloning objects during loading.
|
|
|
+ // This will vary wildly depending on the needs of a project, so take care with
|
|
|
// this step.
|
|
|
// For our example, we will accomplish this by deleting saveable objects.
|
|
|
var saveNodes = GetTree().GetNodesInGroup("Persist");
|
|
|
foreach (Node saveNode in saveNodes)
|
|
|
saveNode.QueueFree();
|
|
|
|
|
|
- // Load the file line by line and process that dictionary to restore the object
|
|
|
+ // Load the file line by line and process that dictionary to restore the object
|
|
|
// it represents.
|
|
|
saveGame.Open("user://savegame.save", (int)File.ModeFlags.Read);
|
|
|
|
|
|
while (saveGame.GetPosition() < save_game.GetLen())
|
|
|
{
|
|
|
// Get the saved dictionary from the next line in the save file
|
|
|
- var nodeData = (Dictionary<object, object>)JSON.Parse(saveGame.GetLine()).Result;
|
|
|
+ var nodeData = (Godot.Collections.Dictionary<string, object>)JSON.Parse(saveGame.GetLine()).Result;
|
|
|
|
|
|
// Firstly, we need to create the object and add it to the tree and set its position.
|
|
|
var newObjectScene = (PackedScene)ResourceLoader.Load(nodeData["Filename"].ToString());
|
|
@@ -297,15 +297,15 @@ Some notes
|
|
|
----------
|
|
|
|
|
|
We have glossed over setting up the game state for loading. It's ultimately up
|
|
|
-to the project creator where much of this logic goes.
|
|
|
-This is often complicated and will need to be heavily
|
|
|
+to the project creator where much of this logic goes.
|
|
|
+This is often complicated and will need to be heavily
|
|
|
customized based on the needs of the individual project.
|
|
|
|
|
|
Additionally, our implementation assumes no Persist objects are children of other
|
|
|
-Persist objects. Otherwise, invalid paths would be created. To
|
|
|
-accommodate nested Persist objects, consider saving objects in stages.
|
|
|
+Persist objects. Otherwise, invalid paths would be created. To
|
|
|
+accommodate nested Persist objects, consider saving objects in stages.
|
|
|
Load parent objects first so they are available for the :ref:`add_child()
|
|
|
-<class_node_method_add_child>`
|
|
|
-call when child objects are loaded. You will also need a way to link
|
|
|
+<class_node_method_add_child>`
|
|
|
+call when child objects are loaded. You will also need a way to link
|
|
|
children to parents as the :ref:`NodePath
|
|
|
<class_nodepath>` will likely be invalid.
|