|
@@ -29,12 +29,22 @@ the GUI or through script. Let's add the relevant nodes using the GUI:
|
|
|
Once this is done when we need to save the game we can get all objects
|
|
|
to save them and then tell them all to save with this script:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var save_nodes = get_tree().get_nodes_in_group("Persist")
|
|
|
- for i in save_nodes:
|
|
|
+ for i in save_nodes:
|
|
|
# Now we can call our save function on each node.
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var saveNodes = GetTree().GetNodesInGroup("Persist");
|
|
|
+ foreach (Node saveNode in saveNodes)
|
|
|
+ {
|
|
|
+ // Now we can call our save function on each node.
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
Serializing
|
|
|
-----------
|
|
|
|
|
@@ -47,7 +57,8 @@ has helper functions for this, such as :ref:`to_json()
|
|
|
contain a save function that returns this data. The save function will look
|
|
|
like this:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
func save():
|
|
|
var save_dict = {
|
|
@@ -72,6 +83,34 @@ like this:
|
|
|
}
|
|
|
return save_dict
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ public Dictionary<object, object> Save()
|
|
|
+ {
|
|
|
+ return new Dictionary<object, object>()
|
|
|
+ {
|
|
|
+ { "Filename", GetFilename() },
|
|
|
+ { "Parent", GetParent().GetPath() },
|
|
|
+ { "PosX", Position.x }, // Vector2 is not supported by JSON
|
|
|
+ { "PosY", Position.y },
|
|
|
+ { "Attack", Attack },
|
|
|
+ { "Defense", Defense },
|
|
|
+ { "CurrentHealth", CurrentHealth },
|
|
|
+ { "MaxHealth", MaxHealth },
|
|
|
+ { "Damage", Damage },
|
|
|
+ { "Regen", Regen },
|
|
|
+ { "Experience", Experience },
|
|
|
+ { "Tnl", Tnl },
|
|
|
+ { "Level", Level },
|
|
|
+ { "AttackGrowth", AttackGrowth },
|
|
|
+ { "DefenseGrowth", DefenseGrowth },
|
|
|
+ { "HealthGrowth", HealthGrowth },
|
|
|
+ { "IsAlive", IsAlive },
|
|
|
+ { "LastAttack", LastAttack }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
This gives us a dictionary with the style
|
|
|
``{ "variable_name":that_variables_value }`` which will be useful when
|
|
|
loading.
|
|
@@ -86,7 +125,8 @@ convert it into an easily stored string and store them in a file. Doing
|
|
|
it this way ensures that each line is its own object so we have an easy
|
|
|
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 path independent.
|
|
|
# Go through everything in the persist category and ask them to return a dict of relevant variables
|
|
@@ -95,25 +135,46 @@ way to pull the data out of the file as well.
|
|
|
save_game.open("user://savegame.save", File.WRITE)
|
|
|
var save_nodes = get_tree().get_nodes_in_group("Persist")
|
|
|
for i in save_nodes:
|
|
|
- var node_data = i.save()
|
|
|
+ var node_data = i.call("save");
|
|
|
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 path independent.
|
|
|
+ // Go through everything in the persist category and ask them to return a dict of relevant variables
|
|
|
+ public void SaveGame()
|
|
|
+ {
|
|
|
+ var saveGame = new File();
|
|
|
+ saveGame.Open("user://savegame.save", (int)File.ModeFlags.Write);
|
|
|
+
|
|
|
+ var saveNodes = GetTree().GetNodesInGroup("Persist");
|
|
|
+ foreach (Node saveNode in saveNodes)
|
|
|
+ {
|
|
|
+ var nodeData = saveNode.Call("Save");
|
|
|
+ saveGame.StoreLine(JSON.Print(nodeData));
|
|
|
+ }
|
|
|
+
|
|
|
+ saveGame.Close();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
Game saved! Loading is fairly simple as well. For that we'll read each
|
|
|
line, use parse_json() to read it back to a dict, and then iterate over
|
|
|
the dict to read our values. But we'll need to first create the object
|
|
|
and we can use the filename and parent values to achieve that. Here is our
|
|
|
load function:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
- # Note: This can be called from anywhere inside the tree. This function is path independent.
|
|
|
+ # 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://save_game.save"):
|
|
|
- return # Error! We don't have a save to load.
|
|
|
+ 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 this step.
|
|
|
+ # 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 savable objects.
|
|
|
var save_nodes = get_tree().get_nodes_in_group("Persist")
|
|
|
for i in save_nodes:
|
|
@@ -134,6 +195,50 @@ load function:
|
|
|
new_object.set(i, current_line[i])
|
|
|
save_game.close()
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // Note: This can be called from anywhere inside the tree. This function is path independent.
|
|
|
+ public void LoadGame()
|
|
|
+ {
|
|
|
+ var saveGame = new File();
|
|
|
+ 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 this step.
|
|
|
+ // For our example, we will accomplish this by deleting savable 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 it represents
|
|
|
+ saveGame.Open("user://savegame.save", (int)File.ModeFlags.Read);
|
|
|
+
|
|
|
+ while (!saveGame.EofReached())
|
|
|
+ {
|
|
|
+ var currentLine = (Dictionary<object, object>)JSON.Parse(saveGame.GetLine()).Result;
|
|
|
+ if (currentLine == null)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // First we need to create the object and add it to the tree and set its position.
|
|
|
+ var newObjectScene = (PackedScene)ResourceLoader.Load(currentLine["Filename"].ToString());
|
|
|
+ var newObject = (Node)newObjectScene.Instance();
|
|
|
+ GetNode(currentLine["Parent"].ToString()).AddChild(newObject);
|
|
|
+ newObject.Set("Position", new Vector2((float)currentLine["PosX"], (float)currentLine["PosY"]));
|
|
|
+
|
|
|
+ // Now we set the remaining variables.
|
|
|
+ foreach (KeyValuePair<object, object> entry in currentLine)
|
|
|
+ {
|
|
|
+ string key = entry.Key.ToString();
|
|
|
+ if (key == "Filename" || key == "Parent" || key == "PosX" || key == "PosY")
|
|
|
+ continue;
|
|
|
+ newObject.Set(key, entry.Value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ saveGame.Close();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
And now we can save and load an arbitrary number of objects laid out
|
|
|
almost anywhere across the scene tree! Each object can store different
|
|
|
data depending on what it needs to save.
|