Explorar o código

Merge pull request #1327 from paulloz/csharp-saving-games

Translate 'Saving games' to C#
Max Hilbrunner %!s(int64=7) %!d(string=hai) anos
pai
achega
84691d0e20
Modificáronse 2 ficheiros con 132 adicións e 11 borrados
  1. 18 2
      tutorials/misc/encrypting_save_games.rst
  2. 114 9
      tutorials/misc/saving_games.rst

+ 18 - 2
tutorials/misc/encrypting_save_games.rst

@@ -36,24 +36,40 @@ The class :ref:`File <class_File>` is simple to use, just open a
 location and read/write data (integers, strings and variants). To create
 an encrypted file, a passphrase must be provided, like this:
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     var f = File.new()
     var err = f.open_encrypted_with_pass("user://savedata.bin", File.WRITE, "mypass")
     f.store_var(game_state)
     f.close()
 
+ .. code-tab:: csharp
+
+    var f = new File();
+    var err = f.OpenEncryptedWithPass("user://savedata.bin", (int)File.ModeFlags.Write, "mypass");
+    f.StoreVar(gameState);
+    f.Close();
+
 This will make the file unreadable to users, but will still not avoid
 them to share savefiles. To solve this, using the device unique id or
 some unique user identifier is needed, for example:
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     var f = File.new()
     var err = f.open_encrypted_with_pass("user://savedata.bin", File.WRITE, OS.get_unique_ID())
     f.store_var(game_state)
     f.close()
 
+ .. code-tab:: csharp
+
+    var f = new File();
+    var err = f.OpenEncryptedWithPass("user://savedata.bin", (int)File.ModeFlags.Write, OS.GetUniqueId());
+    f.StoreVar(gameState);
+    f.Close();
+
 Note that ``OS.get_unique_ID()`` only works on iOS and Android.
 
 This is all! Thanks for your cooperation, citizen.

+ 114 - 9
tutorials/misc/saving_games.rst

@@ -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.