Best Way to Save Large Amount of Data Locally in Unity3D Android

Best way to save large amount of data locally in unity3D android?

NOTE - do not use "StreamWriter" for any reason.

Just use the trivial File.Write commands.

This is a common misunderstanding in Unity!

Regarding this topic, a bad example code was propagated on the www for years. Simply use File.Write.


Regarding the question on this page, "thousands" of entries is absolutely nothing.

Note that for example: any tiny icon image in your app will completely dwarf the size of your name/address data!

(1) extremely easy to save a text file:

// IO crib sheet..
// filePath = Application.persistentDataPath+"/"+fileName;
// check if file exists System.IO.File.Exists(f)
// write to file File.WriteAllText(f,t)
// delete the file if needed File.Delete(f)
// read from a file File.ReadAllText(f)

that's all there is to it.

   string currentText = File.ReadAllText(filePath);

NOTE WELL...........

// filePath = Application.persistentDataPath+"/"+fileName;
// YOU MUST USE "Application.persistentDataPath"
// YOU CANNOT USE ANYTHING ELSE/
// NOTHING OTHER THAN "Application.persistentDataPath" WORKS/
// ALL OTHER OPTIONS FAIL ON ALL PLATFORMS/
// YOU CAN >ONLY< USE Application.persistentDataPath IN UNITY.

Store the info any way you want, probably JSON or csv.

It is dead easy to use Json in Unity, there are 100s of examples on stackoverflow.

example https://stackoverflow.com/a/38535392/294884

(2) once you have say one million items. you can learn about using the local SQL database, which is well worth learning. Do what Łukasz Motyczka says in the comments.

(3) Never use PlayerPrefs - it is much harder and messy!

Best way to save large amount of data locally in unity3D android?

NOTE - do not use "StreamWriter" for any reason.

Just use the trivial File.Write commands.

This is a common misunderstanding in Unity!

Regarding this topic, a bad example code was propagated on the www for years. Simply use File.Write.


Regarding the question on this page, "thousands" of entries is absolutely nothing.

Note that for example: any tiny icon image in your app will completely dwarf the size of your name/address data!

(1) extremely easy to save a text file:

// IO crib sheet..
// filePath = Application.persistentDataPath+"/"+fileName;
// check if file exists System.IO.File.Exists(f)
// write to file File.WriteAllText(f,t)
// delete the file if needed File.Delete(f)
// read from a file File.ReadAllText(f)

that's all there is to it.

   string currentText = File.ReadAllText(filePath);

NOTE WELL...........

// filePath = Application.persistentDataPath+"/"+fileName;
// YOU MUST USE "Application.persistentDataPath"
// YOU CANNOT USE ANYTHING ELSE/
// NOTHING OTHER THAN "Application.persistentDataPath" WORKS/
// ALL OTHER OPTIONS FAIL ON ALL PLATFORMS/
// YOU CAN >ONLY< USE Application.persistentDataPath IN UNITY.

Store the info any way you want, probably JSON or csv.

It is dead easy to use Json in Unity, there are 100s of examples on stackoverflow.

example https://stackoverflow.com/a/38535392/294884

(2) once you have say one million items. you can learn about using the local SQL database, which is well worth learning. Do what Łukasz Motyczka says in the comments.

(3) Never use PlayerPrefs - it is much harder and messy!

What is best Practice to save Game Progress in a Game made in Unity3D along different Versions for Android and iOS

Personnaly, I like to write directly into a file. It's applicable to all platforms, isn't removed on an app update, and you have full control over the way you access the data.
It's easy to add, remove, edit the contents, and easy to handle multiple save states.

You can do it with a simple class, such as this one :

public static class FileUtils
{
public static void WriteToFile(string fileName, string json)
{
var path = GetFilePath(fileName);
var stream = new FileStream(path, FileMode.Create);

using (var writer = new StreamWriter(stream))
writer.Write(json);
}

public static string ReadFromFile(string fileName)
{
var path = GetFilePath(fileName);
if (File.Exists(path))
{
using (var reader = new StreamReader(path))
return reader.ReadToEnd();
}

Debug.LogWarning($"{fileName} not found.");
return null;
}

public static string GetFilePath(string fileName)
{
return Path.Combine(Application.persistentDataPath, fileName);
}
}

And then, you just need a Serializable class or struct to save, here I use JSON :

    [Serializable]
public class UserData
{
public string userName;
public string profilePictureRef;

public UserData(string userName, string profilePictureRef)
{
this.userName = userName;
this.profilePictureRef = profilePictureRef;
}
}

public void SaveUserData(UserData data)
{
var jsonData = JsonUtility.ToJson(data);
FileUtils.WriteToFile(_fileName, jsonData);
}

public void LoadUserData()
{
var jsonData = FileUtils.ReadFromFile(_fileName);
_data = JsonUtility.FromJson<UserData>(jsonData);
}

You can even fetch some JSON from a server and save it locally.
Maybe you'll want to "blur" the contents so that the player can't edit them by hand.

store data persistent for learn app with unity

One of the best JSON serialization libraries is Newtonsoft.Json.

You can use your class and serialize an object to JSON object, and then save it as a string to file.

public static string Serialize(object obj)
{
var settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};
return JsonConvert.SerializeObject(obj, settings);
}

After that you can save it to file in the Application.persistentDataPath directory.

var text = Serialize(data);

var tmpFilePath = Path.Combine(Application.persistentDataPath, "filename");
Directory.CreateDirectory(Path.GetDirectoryName(tmpFilePath));
if (File.Exists(tmpFilePath))
{
File.Delete(tmpFilePath);
}
File.WriteAllText(tmpFilePath, text);

After that you can read the file at any time using File.ReadAllText and deserialize it to an object.

public static T Deserialize<T>(string text)
{
var settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
};

try
{
var result = JsonConvert.DeserializeObject<T>(text, settings);
return result ?? default;
}
catch (Exception e)
{
Debug.Log(e);
}
return default;
}
T result = default;
try
{
if (File.Exists(path))
{
var text = File.ReadAllText(path);
result = Deserialize<T>(text);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
return result;

What is the best way to save game state?

But I heard this way has some issues and not suitable for save.

That's right. On some devices, there are issues with BinaryFormatter. It gets worse when you update or change the class. Your old settings might be lost since the classes non longer match. Sometimes, you get an exception when reading the saved data due to this.

Also, on iOS, you have to add Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes"); or you will have problems with BinaryFormatter.

The best way to save is with PlayerPrefs and Json. You can learn how to do that here.

In my case, save format must be byte array

In this case, you can convert it to json then convert the json string to byte array. You can then use File.WriteAllBytes and File.ReadAllBytes to save and read the byte array.

Here is a Generic class that can be used to save data. Almost the-same as this but it does not use PlayerPrefs. It uses file to save the json data.

DataSaver class:

public class DataSaver
{
//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;
}
}

USAGE:

Example class to Save:

[Serializable]
public class PlayerInfo
{
public List<int> ID = new List<int>();
public List<int> Amounts = new List<int>();
public int life = 0;
public float highScore = 0;
}

Save Data:

PlayerInfo saveData = new PlayerInfo();
saveData.life = 99;
saveData.highScore = 40;

//Save data from PlayerInfo to a file named players
DataSaver.saveData(saveData, "players");

Load Data:

PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>("players");
if (loadedData == null)
{
return;
}

//Display loaded Data
Debug.Log("Life: " + loadedData.life);
Debug.Log("High Score: " + loadedData.highScore);

for (int i = 0; i < loadedData.ID.Count; i++)
{
Debug.Log("ID: " + loadedData.ID[i]);
}
for (int i = 0; i < loadedData.Amounts.Count; i++)
{
Debug.Log("Amounts: " + loadedData.Amounts[i]);
}

Delete Data:

DataSaver.deleteData("players");

Reloading XML asset in Unity

The good news: in Unity it's extremely easy to save/read text files.

The key point...

You must use Application.persistentDataPath (all platforms, all the time - no exceptions). "It's that simple!"

(A) There is utterly no reason, whatsoever, to use any other folders or paths.

(B) Indeed, you simply can not use any other folders or paths.

It's this easy to write and read files in Unity.

using System.IO;
// IO crib sheet..
//
// get the file path:
// f = Application.persistentDataPath+"/"+fileName;
//
// check if file exists: System.IO.File.Exists(f)
// write to file: File.WriteAllText(f,t)
// delete the file if needed: File.Delete(f)
// read from a file: File.ReadAllText(f)

That's all there is to it.

string currentText = File.ReadAllText(filePath);

Regarding the question here, "should I add them manually on the first run"

It's simple ...

public string GetThatXMLStuff()
{
f = Application.persistentDataPath+"/"+"defaults.txt";

// check if it already exists:

if ( ! System.IO.File.Exists(f) )
{
// First run. Put in the default file
string default = "First line of file.\n\n";
File.WriteAllText(f, default);
}

// You know it exists no matter what. Just get the text:
return File.ReadAllText(f);
}

It's really that simple - nothing to it.

How do I save custom variables in Unity3D?

Just to drive home the point

1) In Unity

Totally forget about Serialization. It's that simple.

I can't even be bothered discussing why. It's just how it is!

2) Generally these days in games/mobile you should

Work with JSON

End of story. Fortunately it is incredibly easy.

3) To convert back and fore to JSON,

Do exactly what @Programmer says.

https://stackoverflow.com/a/40097623/294884

4) Finally to save that, honestly there's nothing wrong with:

Simply use PlayerPrefs to save the long JSON string. No worries.

Note that if you DO want to simply save it as a text file, which is fine, it is exceedingly easy to save it as a text file:

Just do this: https://stackoverflow.com/a/35941579/294884

(Notte: never use "streamwriter" in Unity for any reason. It's totally trivial to save/read text files.)

Repeat. Do >> NOT << touch, think about, or use "serialization" in Unity! For any reason, ever!

Enjoy life!



Related Topics



Leave a reply



Submit