Location of Application.persistentDataPath in a build - c#

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;
}
}

In the answer below:
companyname = Company name from the Build Settings
productname = Product name from the Build Settings
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");
}

Related

Copy file from asset folder to external android memory unity

I want to have some of my files in my assets folder to be copied to the android device. How do you do it?
I have tried several ways, like opening the files via debugging, it works in the PC. But when I have transferred it to the android device, it doesn't work or either copy it whatnot.
public void OpenPDF(string filename) //this opens the file I have in my pc. Via debugging in Unity.
{
TextAsset pdfTem = Resources.Load("PDFs/" + filename, typeof(TextAsset)) as TextAsset;
System.IO.File.WriteAllBytes(Application.persistentDataPath + "/" + filename + ".pdf", pdfTem.bytes);
Application.OpenURL(Application.persistentDataPath + "/" + filename + ".pdf");
}
public void openPDFfromSD()
{
Application.OpenURL("/mnt/sdcard/openme.pdf"); //this doesn't open the PDF file I have in my sd card.
}
public void legitOpen(string nameOfFile) //this opens the file I have in my pc. Via debugging in Unity.
{
string realPath = Application.persistentDataPath + "/" + nameOfFile + ".pdf";
if (!System.IO.File.Exists(realPath))
{
if (!System.IO.Directory.Exists(Application.persistentDataPath + "/PDFs/"))
{
System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/PDFs/");
}
WWW reader = new WWW(Application.streamingAssetsPath + "/PDFs/" + realPath);
while (!reader.isDone) { }
System.IO.File.WriteAllBytes(realPath, reader.bytes);
}
Application.OpenURL(realPath);
}
In general you shouldn't use string concatenation directly to build system paths.
Rather always use Path.Combine which automatically uses the correct path separator (/ or \) according to your target platform.
Also later in new WWW you have added both the leading Application.streamingAssetsPath and Application.persistentDataPath already in realPath.
public void OpenPDF(string filename)
{
TextAsset pdfTem = Resources.Load("PDFs/" + filename, typeof(TextAsset)) as TextAsset;
var filePath = Path.Combine(Application.persistentDataPath, filename + ".pdf";
System.IO.File.WriteAllBytes(filePath), pdfTem.bytes);
Application.OpenURL(filePath);
}
public void legitOpen(string nameOfFile)
{
string realPath = Path.Combine(Application.persistentDataPath, nameOfFile + ".pdf");
if (!System.IO.File.Exists(realPath))
{
if (!System.IO.Directory.Exists(Path.Combine(Application.persistentDataPath, "PDFs"))
{
System.IO.Directory.CreateDirectory(Path.Combine(Application.persistentDataPath, "PDFs"));
}
WWW reader = new WWW(Path.Combine(Application.streamingAssetsPath, "PDFs", nameOfFile + ".pdf");
while (!reader.isDone) { }
System.IO.File.WriteAllBytes(realPath, reader.bytes);
}
Application.OpenURL(realPath);
}
Btw if you want to prevent your app from completely freezing until the loading is done I would recommend to use a Coroutine and UnityWebRequest.Get like
public void legitOpen(string nameOfFile)
{
StartCoroutine(legitOpenRoutine(nameOfFile));
}
private IEnumerator legitOpenRoutine(string nameOfFile)
{
string realPath = Path.Combine(Application.persistentDataPath, nameOfFile + ".pdf");
if (!System.IO.File.Exists(realPath))
{
if (!System.IO.Directory.Exists(Path.Combine(Application.persistentDataPath, "PDFs"))
{
System.IO.Directory.CreateDirectory(Path.Combine(Application.persistentDataPath, "PDFs"));
}
using (var reader = new UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, "PDFs", nameOfFile + ".pdf"))
{
yield return reader.SendWebRequest();
if (webRequest.isNetworkError)
{
Debug.Log(pages[page] + ": Error: " + webRequest.error);
return;
}
System.IO.File.WriteAllBytes(realPath, reader.bytes);
}
}
Application.OpenURL(realPath);
}
Or even completely use an async method using CopyToAsync
public void legitOpen(string nameOfFile)
{
legitOpenAsync(nameOfFile);
}
private async void legitOpenAsync(string nameOfFile)
{
var realPath = Path.Combine(Application.persistentDataPath, nameOfFile + ".pdf");
var pdfPath = Path.Combine(Application.persistentDataPath, "PDFs");
if (!System.IO.File.Exists(realPath))
{
if (!System.IO.Directory.Exists(pdfPath)
{
System.IO.Directory.CreateDirectory(pdfPath);
}
using(var sourceFile = File.Open(Path.Combine(Application.streamingAssetsPath, "PDFs", nameOfFile + ".pdf"), FileMode.Open, FileAccess.Read, FileShare.Read)
{
using(var targetFile = File.Open(realPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
{
await sourceFile.CopyToAsync(targetFile);
}
}
}
Application.OpenURL(realPath);
}
However
note that Application.OpenURL:
Android: Due security changes in Android 7.0 (More information), Application.OpenURL can no longer be used for opening local app files, you need to use FileProvider which allows you to share files with other applications.
and
iOS: Application.OpenURL cannot be used for opening local files.
Therefore this won't work at all
public void openPDFfromSD()
{
Application.OpenURL("/mnt/sdcard/openme.pdf");
}

Saving bitmap to gallery in Android

I thought that should be simple, yet I can't figure it out.
I keep getting the error: System.IO.DirectoryNotFoundException: Could not find a part of the path "/storage/emulated/0/Pictures/Screenshots/name.jpg".
The code:
string root = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).Path;
File myDir = new File(root + "/Screenshots");
myDir.Mkdirs();
string timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").Format(new Date());
string fname = CommunicationHandler.GetNickname() + "|" + timeStamp + ".jpg";
File file = new File(myDir, fname);
if (file.Exists())
file.Delete();
try
{
using (System.IO.Stream outStream = System.IO.File.Create(file.Path))
{
finalBitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, outStream);
outStream.Flush();
outStream.Close();
}
}
catch (Exception e)
{
Toast.MakeText(Activity, e.ToString(), ToastLength.Long).Show();
}
Also, I can't access manually to /storage/emulated/0..
Why can't I manage to save the bitmap to my phone gallery? What's the problem in the code above?
If you want to create a new directory, you can use System.IO.Directory.CreateDirectory(root); to create it.
//create a directory called MyCamera
string root = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDcim).ToString() + "/MyCamera/";
//create the Directory
System.IO.Directory.CreateDirectory(root);
As taken from here:
MediaScannerConnection.scanFile(this, new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
Also, note that you could just add an image to the gallery via a simple line:
MediaStore.Images.Media.insertImage(applicationContext.getContentResolver(), IMAGE ,"nameofimage" , "description");

C# Get files Outlook Attachments single-multiple selected files Win 7, Win 10

I have re-edited my question since the problem lies elsewhere.
I have this piece of code to drop the files from outlook (single or multiple) at specific win form. On windows 7 stations the copy is made, but on windows 10 cannot get the list of filename from class.
public class OutlookDataObject : System.Windows.Forms.IDataObject
Class shown on this post
This class is working on Working code for win 7 but no filename return on windwos 10. This huge class is way over my understanding.
There is a simple way to get from outlook the selected attachements to prepare them to drop ?
private void btn_Home_DragDrop(object sender, DragEventArgs e)
{
bool debug = true;
if (debug) { txt_FileInfo.AppendText("Entering drop method " + Environment.NewLine); }
folderBrowserDialog1.SelectedPath = LastSelectedFolder.GlobalVar;
if (debug)
{ txt_FileInfo.AppendText("Get last path " + Environment.NewLine); }
folderBrowserDialog1.Description = "Drop the files";
if (debug)
{ txt_FileInfo.AppendText("Show folder dialog " + Environment.NewLine); }
if (folderBrowserDialog1.ShowDialog() != DialogResult.OK)
{
return;
}
LastSelectedFolder.GlobalVar = folderBrowserDialog1.SelectedPath.ToString();
if (debug)
{ txt_FileInfo.AppendText("Path is selected " + LastSelectedFolder.GlobalVar + Environment.NewLine); }
string[] fileNames = null;
if (debug)
{ txt_FileInfo.AppendText("Prepare to transfer " + Environment.NewLine); }
if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
{
if (debug)
{ txt_FileInfo.AppendText("DataFormats.FileDrop " + Environment.NewLine); }
fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string fileName in fileNames)
{
// do what you are going to do with each filename
string destinationFile = Path.Combine(folderBrowserDialog1.SelectedPath, Path.GetFileName(fileName));
if (debug)
{ txt_FileInfo.AppendText("Destination File " + destinationFile + Environment.NewLine); }
if (Operation.CopyFile(fileName, destinationFile, ci))
{
txt_FileInfo.AppendText("File have been copied to " + destinationFile + Environment.NewLine);
}
}
}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
if (debug)
{ txt_FileInfo.AppendText("FileGroupDescriptor " + Environment.NewLine); }
OutlookDataObject dataObject = new OutlookDataObject(e.Data);
string[] filenames = (string[])dataObject.GetData("FileGroupDescriptor");
for (int fileIndex = 0; fileIndex < filenames.Length; fileIndex++)
{
if (debug)
{ txt_FileInfo.AppendText("Files in attachement " + filenames[fileIndex] + Environment.NewLine); }
string path = Path.GetTempPath();
// put the zip file into the temp directory
string theFile = path + filenames[fileIndex].ToString();
// create the full-path name
if (debug)
{ txt_FileInfo.AppendText("Get temp Path " + theFile + Environment.NewLine); }
//
// Second step: we have the file name.
// Now we need to get the actual raw
// data for the attached file and copy it to disk so we work on it.
//
// get the actual raw file into memory
MemoryStream ms = (MemoryStream)e.Data.GetData(
"FileContents", true);
// allocate enough bytes to hold the raw data
byte[] fileBytes = new byte[ms.Length];
// set starting position at first byte and read in the raw data
ms.Position = 0;
ms.Read(fileBytes, 0, (int)ms.Length);
// create a file and save the raw zip file to it
FileStream fs = new FileStream(theFile, FileMode.Create);
fs.Write(fileBytes, 0, (int)fileBytes.Length);
fs.Close(); // close the file
FileInfo tempFile = new FileInfo(theFile);
// always good to make sure we actually created the file
if (tempFile.Exists == true)
{
// for now, just delete what we created
string fileName = tempFile.FullName;
string destinationFile = Path.Combine(folderBrowserDialog1.SelectedPath, Path.GetFileName(fileName));
if (debug)
{ txt_FileInfo.AppendText("destinationFile " + destinationFile + Environment.NewLine); }
if (debug)
{ txt_FileInfo.AppendText("Prepare to copy " + destinationFile + Environment.NewLine); }
if (Operation.CopyFile(fileName, destinationFile, ci))
{
txt_FileInfo.AppendText("File have been copied to " + destinationFile + Environment.NewLine);
}
else
{
if (debug)
{ txt_FileInfo.AppendText("Copy failed " + " Source " + fileName + " Destination " + destinationFile + Environment.NewLine); }
}
tempFile.Delete();
if (debug)
{ txt_FileInfo.AppendText("Delete temp file " + tempFile + Environment.NewLine); }
}
else
{ Trace.WriteLine("File was not created!"); }
// catch (Exception ex)
//{
// Trace.WriteLine("Error in DragDrop function: " + ex.Message);
// // don't use MessageBox here - Outlook or Explorer is waiting !
//}
}
}
}
I will replay here quote from here. For above class to work on win 8 + couple of line to be changed (from int to long)
from:
IntPtr fileDescriptorPointer = (IntPtr)((int)fileGroupDescriptorWPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));
to
IntPtr fileDescriptorPointer = (IntPtr)((long)fileGroupDescriptorWPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));
from:
fileDescriptorPointer = (IntPtr)((int)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
to
fileDescriptorPointer = (IntPtr)((long)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
Use this:
MemoryStream ms = (MemoryStream)dataObject.GetData("FileContents", fileIndex);
Instead of this:
MemoryStream ms = (MemoryStream)dataObject.GetData("FileContents", true);
So it parses every files.
EDIT:
Actually, it doesn't work neither unless program is compiled in Debug rather than Release... It will only work in Debug for some reason

Where does persistentDataPath point to when WRITE_EXTERNAL_STORAGE permission is enabled on an Android device? [duplicate]

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;
}
}
In the answer below:
companyname = Company name from the Build Settings
productname = Product name from the Build Settings
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");
}

What is the best way to save game state?

I find the best way to save game data in Unity3D Game engine.
At first, I serialize objects using BinaryFormatter.
But I heard this way has some issues and is not suitable for save.
So, What is the best or recommended way for saving game state?
In my case, save format must be byte array.
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");
I know this post is old, but in case other users also find it while searching for save strategies, remember:
PlayerPrefs is not for storing game state. It is explicitly named "PlayerPrefs" to indicate its use: storing player preferences. It is essentially plain text. It can easily be located, opened, and edited by any player. This may not be a concern for all developers, but it will matter to many whose games are competitive.
Use PlayerPrefs for Options menu settings like volume sliders and graphics settings: things where you don't care that the player can set and change them at will.
Use I/O and serialization for saving game data, or send it to a server as Json. These methods are more secure than PlayerPrefs, even if you encrypt the data before saving.

Categories

Resources