Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Changing save data location

Discussion in 'Scripting' started by GobernIndustries, May 31, 2022.

  1. GobernIndustries

    GobernIndustries

    Joined:
    Apr 15, 2022
    Posts:
    5
    Hi everyone. So, I'm a bit of a newcomer when it comes to coding in c# and was wondering if I could get some help with my saving and loading system that I've been working on for a bit. The saving and loading works perfectly, but what I'm trying to do is that the player can save and load multiple worlds. I already have a script that will create a new directory every time a new world has been created, but what I'm trying to figure out is how to save that data to that directory (basically change the path) and load it later. I had an idea of creating a string and changing the value to the name of the directory when the player so chooses. Is there a way to do this?

    Code (CSharp):
    1. using System.IO;
    2. using UnityEngine;
    3. using System.Runtime.Serialization.Formatters.Binary;
    4.  
    5. public static class SaveSystem
    6. {
    7.  
    8.    
    9.    
    10.     public static void SavePlayer (PlayerMovement player)
    11.     {
    12.         BinaryFormatter formatter = new BinaryFormatter();
    13.         string path = Application.persistentDataPath + "/player.vdplane";
    14.         using (FileStream stream = new FileStream(path, FileMode.Create))
    15.         {
    16.             PlayerData data = new PlayerData(player);
    17.  
    18.             formatter.Serialize(stream, data);
    19.             stream.Close();
    20.             Debug.Log(path);
    21.         }
    22.  
    23.     public static PlayerData LoadPlayer ()
    24.     {
    25.         string path = Application.persistentDataPath + "/player.vdplane";
    26.         if (File.Exists(path))
    27.         {
    28.             BinaryFormatter formatter = new BinaryFormatter();
    29.             using (FileStream stream = new FileStream(path, FileMode.Open))
    30.             {
    31.                 PlayerData data = formatter.Deserialize(stream) as PlayerData;
    32.                 stream.Close();
    33.  
    34.                 return data;
    35.             }
    36.            
    37.         }
    38.         else
    39.         {
    40.             Debug.LogError("Load player save file not found in" + path);
    41.             return null;
    42.         }
    43.     }
    44.  
    45.    
    46. }
    47.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,969
    Your best bet is to remove ALL instances of Application.persistentDataPath from the above code and instead provide a new static class that provides the correct directory. (Obviously derived from this same persistent path!)

    For instance, if you called that class ActiveSaveSlot, then it might have a method to provide the desired directory for the current slot, and it would also have logic to ensure that directory exists first, let you select other slots, tell you how many slots there are, find a free slot, etc.

    This breaks apart the logic for saving / loading the data (which should not care which slot it is in) from the decision of what slot to select, which might be further driven by the user interface (for example), or by other game logic, such as "is this the auto save slot or a player save slot?".
     
  3. GobernIndustries

    GobernIndustries

    Joined:
    Apr 15, 2022
    Posts:
    5
    Could you possibly give an example? I understood what you meant but I'm still a little confused on how to implement this.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,969
    Not sure how I can say it any clearer than the above.

    Code (csharp):
    1. public static class SaveSlot
    2. {
    3.   public static string GetSavePath()
    4.   {
    5.     return Application.persistentDataPath;
    6.   }
    7. }
    Now... call the above (
    SaveSlot.GetSavePath()
    )instead of using Application.persistentDataPath;

    When that works perfectly, start putting logic for directories (eg "slots") into the above class.
     
  5. GobernIndustries

    GobernIndustries

    Joined:
    Apr 15, 2022
    Posts:
    5
    What would I have to do if I wanted to get the data for selecting the directory from Unity's UI system since static classes can't interact with game objects? Right now, for loading at least I have a piece of code that gets the name of all of the directories and using that list the player would select the game they want to play.
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using System.IO;
    6. public class LoadGameFolders : MonoBehaviour {
    7.      public TMPro.TMP_Dropdown GameList;
    8.    
    9.  
    10.  
    11.      void Awake () {
    12.        
    13.          DirectoryInfo dir = new DirectoryInfo(Application.persistentDataPath);
    14.          DirectoryInfo[] info = dir.GetDirectories ();
    15.          foreach (DirectoryInfo f in info)
    16.          {
    17.              List<string> DDList = new List<string>() { f.Name };
    18.              GameList.AddOptions(DDList);
    19.          }
    20.      }
    21.  
    22.     public void TellSelection()
    23.     {
    24.         Debug.Log(GameList.options[GameList.value].text);
    25.         string SelectedWorld = GameList.options[GameList.value].text;
    26.        
    27.     }
    28.  
    29.     void Start ()
    30.     {
    31.         //TellSelection(gameObject);
    32.     }
    33. }
    34.  
    This is the code for it but this obviously isn't usable in a static class.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,969
    Same as always: make a MonoBehaviour class to interoperate with the UI and to then change the state of the static class. Those two things should always be separate anyway... the save slot thing should not care HOW you select a slot (and it should especially never care about UI details in general... what if you change the UI completely? The save system shouldn't change!), it just provides the slot path.
     
  7. GobernIndustries

    GobernIndustries

    Joined:
    Apr 15, 2022
    Posts:
    5
    Alright, I think I'm beginning to understand, but still running into some problems. Idk I could just be tired or something but in anycase I have this now in my monobehaviour class:
    Code (CSharp):
    1. public void TellSelection()
    2.     {
    3.         Debug.Log(GameList.options[GameList.value].text);
    4.         GameList.options[GameList.value].text = input;
    5.         input = SaveSlot(secInput);
    6.  
    7.  
    8.     }
    and this in my static class

    Code (CSharp):
    1. public static string secInput;
    2.  
    3.     public static string GetSavePath()
    4.     {
    5.        
    6.         return Application.persistentDataPath + "/" + secInput;
    7.     }
    Now its just communication between the two scripts. How do I do that since input = SaveSlot(secInput); isn't working? I am aware that that is not the right way to do it but I'm not sure what the right way is.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,969
    What is this noise?

    Don't you mean to assign
    input
    into the
    secInput
    field?

    Code (csharp):
    1. SaveSlot.secInput = input;
    Keep the steps straight in your mind:

    button input -> selection of a slot

    desire to save -> get that slot -> do the save

    etc.
     
  9. GobernIndustries

    GobernIndustries

    Joined:
    Apr 15, 2022
    Posts:
    5
    Alright, I think I get it now. Thanks for your help.
     
    Kurt-Dekker likes this.