5

When I run my game from the editor and save load data, I find that data in: C:\Users\User\AppData\LocalLow\DefaultCompany\projectname\data.

When I build it and get an executable, that data still loads fine, but if I save and restart, it does not get saved. However it does when I launch it from the Editor.

Is this a fault in my code or is the files somewhere else when I don't run it from Unity Editor?

Later when I export the game to launch it, I have persistent data I Json files that have to come with it for the game to work.

For clarity, here is the class that handles save/load of Json:

public class DataHandler
{
    //Save Data
    public static void saveData<T>(T dataToSave, string dataFileName)
    {
        string tempPath = Path.Combine(Application.persistentDataPath, "data");
        tempPath = Path.Combine(tempPath, dataFileName + ".txt");

        //Convert To Json then to bytes
        string jsonData = JsonUtility.ToJson(dataToSave, true);
        byte[] jsonByte = Encoding.ASCII.GetBytes(jsonData);

        //Create Directory if it does not exist
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
        }
        //Debug.Log(path);

        try
        {
            File.WriteAllBytes(tempPath, jsonByte);
            Debug.Log("Saved Data to: " + tempPath.Replace("/", "\\"));
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To PlayerInfo Data to: " + tempPath.Replace("/", "\\"));
            Debug.LogWarning("Error: " + e.Message);
        }
    }

    //Load Data
    public static T loadData<T>(string dataFileName)
    {
        string tempPath = Path.Combine(Application.persistentDataPath, "data");
        tempPath = Path.Combine(tempPath, dataFileName + ".txt");

        //Exit if Directory or File does not exist
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Debug.LogWarning("Directory does not exist");
            return default(T);
        }

        if (!File.Exists(tempPath))
        {
            Debug.Log("File does not exist");
            return default(T);
        }

        //Load saved Json
        byte[] jsonByte = null;
        try
        {
            jsonByte = File.ReadAllBytes(tempPath);
            Debug.Log("Loaded Data from: " + tempPath.Replace("/", "\\"));
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To Load Data from: " + tempPath.Replace("/", "\\"));
            Debug.LogWarning("Error: " + e.Message);
        }

        //Convert to json string
        string jsonData = Encoding.ASCII.GetString(jsonByte);

        //Convert to Object
        object resultValue = JsonUtility.FromJson<T>(jsonData);
        return (T)Convert.ChangeType(resultValue, typeof(T));
    }

    public static bool deleteData(string dataFileName)
    {
        bool success = false;

        //Load Data
        string tempPath = Path.Combine(Application.persistentDataPath, "data");
        tempPath = Path.Combine(tempPath, dataFileName + ".txt");

        //Exit if Directory or File does not exist
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Debug.LogWarning("Directory does not exist");
            return false;
        }

        if (!File.Exists(tempPath))
        {
            Debug.Log("File does not exist");
            return false;
        }

        try
        {
            File.Delete(tempPath);
            Debug.Log("Data deleted from: " + tempPath.Replace("/", "\\"));
            success = true;
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To Delete Data: " + e.Message);
        }

        return success;
    }
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
Green_qaue
  • 3,561
  • 11
  • 47
  • 89
  • Which platform are you building this for? – Programmer Jun 07 '17 at 18:19
  • For standalone builds I use `Application.dataPath` which is adjacent to the executable file itself. I then use a subfolder (e.g. `Application.dataPath + "RuntimeData"`) and save there. – Draco18s no longer trusts SE Jun 07 '17 at 18:22
  • I am building exclusively for PC. Just want to get an understanding of where everything goes after a "real" installation. Still very new to Unity – Green_qaue Jun 07 '17 at 18:27
  • If you have a file that contains the default value you should put it in the Resources folder. You then check if this is the first time the app is running. If so, read from the Resources folder then copy those default values to the `Application.persistentDataPath` with the `DataHandler` class. If this is not the first time then use the `DataHandler` class to read from your old value. See the last part of my answer for how to do this. – Programmer Jun 07 '17 at 20:16

1 Answers1

18

In the answer below:

  • companyname = Company name from the Build Settings
  • productname = Product name from the Build Settings

enter image description here

Windows:

C:\Users\<userprofile>\AppData\LocalLow\<companyname>\<productname>

Windows Store:

%userprofile%\AppData\Local\Packages\<productname>\LocalState

Mac:

~/Library/Application Support/companyname/productname

older version of Unity on Mac:

  • ~/Library/Caches folder

  • ~/Library/Application Support/unity.companyname.productname.

Linux:

$XDG_CONFIG_HOME/unity3d/<companyname>/<productname>

which is the-same as

~/.config/unity3d/<companyname>/<productname>

Android:

/Data/Data/com.<companyname>.<productname>/files

with SD card on the Android device:

/storage/sdcard0/Android/data/com.<companyname>.<productname>/files

iOS:

/var/mobile/Containers/Data/Application/<RandomFolderName>/Documents

Example of the RandomFolderName full name:

/var/mobile/Containers/Data/Application/<055811B9-D125-41B1-A078-F898B06F8C58>/Documents

On iOS, you will be given access to the app's sandbox which is the Document folder. You must create folder inside this directory in order to create a new file inside it.


If you have a default data values in a json file, put the file in Assets/Resources folder so that it will be read-only then read it with TextAsset. When the game loads, you can use PlayerPrefs to check if this is the first time the game being loaded.

If this is the first time, use the value from TextAsset.text. If it is not use then value saved with the DataHandler class.

Roughly something like this:

if (PlayerPrefs.GetInt("FIRSTTIMEOPENING", 1) == 1)
{
    Debug.Log("First Time Opening");

    //Set first time opening to false
    PlayerPrefs.SetInt("FIRSTTIMEOPENING", 0);

    //USE TextAsset to load data
    TextAsset txtAsset = (TextAsset)Resources.Load("player", typeof(TextAsset));
    string tileFile = txtAsset.text;
    PlayerInfo pInfo = JsonUtility.FromJson<PlayerInfo>(tileFile);
}
else
{
    Debug.Log("NOT First Time Opening");

    //USE DataHandler to load data
    PlayerInfo pInfo = DataHandler.loadData<PlayerInfo>("player");
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 2
    Thanks, great answer. So how come I only need to read from the Resources on firstTimeOpening? Also, does this go for Sprites as well? because I set plenty of sprite images using `Resources.Load` if I need them to change at runtime. – Green_qaue Jun 07 '17 at 20:39
  • If you have default values in a json file that your game will use....For example, all player live starts from 100. If you have that 100 and over many values inside a json file instead of hardcoding it in your code, you need a way read that json file. You also don't want this default value file to be modified. So you put it in the Resources folder. On the first time your app is ran, you load all your Game default data values from that file and save it with the `DataHandler` class. – Programmer Jun 07 '17 at 20:51
  • Now, you don't need the load that default data from the resources again until player decides to reset the game. The reason you don't want to load the default value again is because the value may have been changed by the player. You now want to be loading values saved by the `DataHandler` class since that's the latest value. – Programmer Jun 07 '17 at 20:53
  • It does :) I keep all my Item data in a Json, which is an Array of several hundred items, however this data will never be changed or effected by the game, so maybe I should always load it from Resources. Currently I just load/save that the same way I handle modified data (playerData, Inventory). Ill make sure to change that then. – Green_qaue Jun 08 '17 at 07:38
  • 1
    Works like a charm btw, makes a lot more sense to save "static" data in the Resources folder. Thanks!! – Green_qaue Jun 08 '17 at 07:54
  • Good answer however on iOS you don't need to create folder inside Documents because Unity will do it for you. So you can just directly create file inside persistentData. – Skylin R Jan 17 '20 at 09:01