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.
Related
I'm trying to extract an ISO to a folder with the same name without .iso on the end.
I'm having a problem with winrar as it will not start the extract when I start up with the seach starting in the folder with the ISO.
UPDATED with answer code
private void ExtractISO(string toExtract, string folderName)
{
// reads the ISO
CDReader Reader = new CDReader(File.Open(toExtract, FileMode.Open), true);
// passes the root directory the folder name and the folder to extract
ExtractDirectory(Reader.Root, folderName /*+ Path.GetFileNameWithoutExtension(toExtract)*/ + "\\", "");
// clears reader and frees memory
Reader.Dispose();
}
private void ExtractDirectory(DiscDirectoryInfo Dinfo, string RootPath, string PathinISO)
{
if (!string.IsNullOrWhiteSpace(PathinISO))
{
PathinISO += "\\" + Dinfo.Name;
}
RootPath += "\\" + Dinfo.Name;
AppendDirectory(RootPath);
foreach (DiscDirectoryInfo dinfo in Dinfo.GetDirectories())
{
ExtractDirectory(dinfo, RootPath, PathinISO);
}
foreach (DiscFileInfo finfo in Dinfo.GetFiles())
{
using (Stream FileStr = finfo.OpenRead())
{
using (FileStream Fs = File.Create(RootPath + "\\" + finfo.Name)) // Here you can Set the BufferSize Also e.g. File.Create(RootPath + "\\" + finfo.Name, 4 * 1024)
{
FileStr.CopyTo(Fs, 4 * 1024); // Buffer Size is 4 * 1024 but you can modify it in your code as per your need
}
}
}
}
static void AppendDirectory(string path)
{
try
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
catch (DirectoryNotFoundException Ex)
{
AppendDirectory(Path.GetDirectoryName(path));
}
catch (PathTooLongException Ex)
{
AppendDirectory(Path.GetDirectoryName(path));
}
}
The user selects the folder to extract (.ISO) toExtract. I then use it in the Process.Start() in the background worker. That just seems to open the mounting software and doesn't extract the ISO to the desired folder name.
Thanks in advance for your help.
Or if anyone could give me a batch to extract the ISO instead and to call it from c# passing toExtract and the folder name that would be helpful too.
Thanks
If external Class Libraries are OK!
Then use SevenZipSharp or .NET DiscUtils to extract ISO's...
These two ClassLibraries can manage ISO and Extract them!
For DiscUtils you can find some codes for ISO Management [CDReader Class] at the Link I provided.
But For SevenZipSharp, Please Explore the ClassLibrary source and find the Code to Extract or Google to find it!
To get the Name of the folder just use Path.GetFileNameWithoutExtension((string)ISOFileName) which will return "ISOFile" for an iso named "ISOFile.iso". And then you can use it with your desired path.
UPDATE
Code To Extract ISO Image with DiscUtils :
using DiscUtils;
using DiscUtils.Iso9660;
void ExtractISO(string ISOName, string ExtractionPath)
{
using (FileStream ISOStream = File.Open(ISOName, FileMode.Open))
{
CDReader Reader = new CDReader(ISOStream, true, true);
ExtractDirectory(Reader.Root, ExtractionPath + Path.GetFileNameWithoutExtension(ISOName) + "\\", "");
Reader.Dispose();
}
}
void ExtractDirectory(DiscDirectoryInfo Dinfo, string RootPath, string PathinISO)
{
if (!string.IsNullOrWhiteSpace(PathinISO))
{
PathinISO += "\\" + Dinfo.Name;
}
RootPath += "\\" + Dinfo.Name;
AppendDirectory(RootPath);
foreach (DiscDirectoryInfo dinfo in Dinfo.GetDirectories())
{
ExtractDirectory(dinfo, RootPath, PathinISO);
}
foreach (DiscFileInfo finfo in Dinfo.GetFiles())
{
using (Stream FileStr = finfo.OpenRead())
{
using (FileStream Fs = File.Create(RootPath + "\\" + finfo.Name)) // Here you can Set the BufferSize Also e.g. File.Create(RootPath + "\\" + finfo.Name, 4 * 1024)
{
FileStr.CopyTo(Fs, 4 * 1024); // Buffer Size is 4 * 1024 but you can modify it in your code as per your need
}
}
}
}
static void AppendDirectory(string path)
{
try
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
catch (DirectoryNotFoundException Ex)
{
AppendDirectory(Path.GetDirectoryName(path));
}
catch (PathTooLongException Exx)
{
AppendDirectory(Path.GetDirectoryName(path));
}
}
Use It with Like This :
ExtractISO(ISOFileName, Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\");
Working! Tested By Me!
And Of Course You can always add more Optimization to the code...
This Code is Just a Basic One!
For UDF or for making Windows ISO Files after servicing(DISM) with out needs the above accepted answer is not working for me so i tried this working method with DiscUtils
using DiscUtils;
public static void ReadIsoFile(string sIsoFile, string sDestinationRootPath)
{
Stream streamIsoFile = null;
try
{
streamIsoFile = new FileStream(sIsoFile, FileMode.Open);
DiscUtils.FileSystemInfo[] fsia = FileSystemManager.DetectDefaultFileSystems(streamIsoFile);
if (fsia.Length < 1)
{
MessageBox.Show("No valid disc file system detected.");
}
else
{
DiscFileSystem dfs = fsia[0].Open(streamIsoFile);
ReadIsoFolder(dfs, #"", sDestinationRootPath);
return;
}
}
finally
{
if (streamIsoFile != null)
{
streamIsoFile.Close();
}
}
}
public static void ReadIsoFolder(DiscFileSystem cdReader, string sIsoPath, string sDestinationRootPath)
{
try
{
string[] saFiles = cdReader.GetFiles(sIsoPath);
foreach (string sFile in saFiles)
{
DiscFileInfo dfiIso = cdReader.GetFileInfo(sFile);
string sDestinationPath = Path.Combine(sDestinationRootPath, dfiIso.DirectoryName.Substring(0, dfiIso.DirectoryName.Length - 1));
if (!Directory.Exists(sDestinationPath))
{
Directory.CreateDirectory(sDestinationPath);
}
string sDestinationFile = Path.Combine(sDestinationPath, dfiIso.Name);
SparseStream streamIsoFile = cdReader.OpenFile(sFile, FileMode.Open);
FileStream fsDest = new FileStream(sDestinationFile, FileMode.Create);
byte[] baData = new byte[0x4000];
while (true)
{
int nReadCount = streamIsoFile.Read(baData, 0, baData.Length);
if (nReadCount < 1)
{
break;
}
else
{
fsDest.Write(baData, 0, nReadCount);
}
}
streamIsoFile.Close();
fsDest.Close();
}
string[] saDirectories = cdReader.GetDirectories(sIsoPath);
foreach (string sDirectory in saDirectories)
{
ReadIsoFolder(cdReader, sDirectory, sDestinationRootPath);
}
return;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
it has extracted from a application source ISOReader but modified for my requirements
total source is available at http://www.java2s.com/Open-Source/CSharp_Free_CodeDownload/i/isoreader.zip
Try this:
string Desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
Process.Start("Winrar.exe", string.Format("x {0} {1}",
Desktop + "\\test.rar",
Desktop + "\\SomeFolder"));
That would extract the file test.rar to the folder SomeFolder. You can change the .rar extention to .iso, it'll work the same.
As far as I can see in your current code, there is no command given to extract a file, and no path to the file that has to be extracted. Try this example and let me know if it works =]
P.S. If you'd like to hide the extracting screen, you can set the YourProcessInfo.WindowStyle to ProcessWindowStyle.Hidden.
I hace confrunted recently with this kind of .iso extraction issue. After trying several methods, 7zip did the job for me, you just have to make sure that the latest version of 7zip is installed on your system. Maybe it will help
try
{
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
cmd.Start();
cmd.StandardInput.WriteLine("C:");
//Console.WriteLine(cmd.StandardOutput.Read());
cmd.StandardInput.Flush();
cmd.StandardInput.WriteLine("cd C:\\\"Program Files\"\\7-Zip\\");
//Console.WriteLine(cmd.StandardOutput.ReadToEnd());
cmd.StandardInput.Flush();
cmd.StandardInput.WriteLine(string.Format("7z x -y -o{0} {1}", source, copyISOLocation.TempIsoPath));
//Console.WriteLine(cmd.StandardOutput.ReadToEnd());
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
Console.WriteLine(cmd.StandardOutput.ReadToEnd());
}
catch (Exception e)
{
Console.WriteLine(e.Message + "\n" + e.StackTrace);
if (e.InnerException != null)
{
Console.WriteLine(e.InnerException.Message + "\n" + e.InnerException.StackTrace);
}
}
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
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");
}
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");
}
In my program, I want to save some screenshots and load them later on to compute somethings. I created a methode to compute the image names:
static public string generatePhotoName (string cameraName, float time)
{
return "G:/Data/unity/cameraDemo/" +
DataController.measurmentPath +
"/photos" + "/" +
cameraName + "/" +
time.ToString () + "_" + cameraName + ".png";
}
This worked fine for saving, but when I try to load an image, File.Exists (filePath)returns false.
But when I hardcoded the filepath, loading works fine too:
static public string generatePhotoName (string cameraName, float time)
{
return "G:/Data/unity/cameraDemo/demo/photos/Camera/test.png";
}
It even works with "real" image names(i.e. 3.827817_Camera.png).
Using Path.Combine(...) and changing "/" to "\" did not change anything...
// edit: this is my load methode
static public Texture2D loadPhotoToTexture (string filePath)
{
Texture2D tex = null;
byte[] fileData;
if (File.Exists (filePath)) {
fileData = File.ReadAllBytes (filePath);
tex = new Texture2D (2, 2);
tex.LoadImage (fileData); //..this will auto-resize the texture dimensions.
} else {
Debug.Log (filePath + " does not exist");
}
return tex;
}`
// edit2: some more code
This is how I call the methode
Texture2D photo = DataController.loadPhotoToTexture(photoData.getFileName ());
And this is my class PhotoData
public class PhotoData : BaseData
{
private string _cameraName;
public string cameraName {
get { return _cameraName; }
set { _cameraName = value; }
}
private float _time;
public float time {
get { return _time; }
set { _time = value; }
}
public PhotoData ()
{
}
public PhotoData (string cameraName, float time)
{
_cameraName = cameraName;
_time = time;
}
public string getFileName ()
{
return PlayerController.generatePhotoName (_cameraName, _time);
}
}
The problem was, that I tried to save and load the screenshots in one Update()-call.
I fixed it by changing it to right-click to take and save a screenshot and left-click to load the screenshot.
If you want to store images to a certain file on disk, not inside your game folder structure, do this: create a folder, and store its location (filepath). For example:
string screenshotFileName = time.ToString () + "_" + cameraName + ".png"
string screenshotDirectory;
screenshotDirectory = #"C:\someFolder\anotherFolder\";
try { Directory.CreateDirectory(directory); }
catch (Exception e)
{ //you should catch each exception separately
if (e is DirectoryNotFoundException || e is UnauthorizedAccessException)
Debug.LogError("OMG PANIC");
}
FileInfo filepath = new FileInfo(screenshotDirectory+screenshotFileName);
if(filepath.Exists)
{
//load the file
}
You could also simply create a folder, with a relative path, which will be in the same folder as your executable, by changing the screenshotDirectory to
screenshotDirectory = #"screenshots\"+cameraName+#"\";
Edit:
You seem to load the texture correctly. Are you assigning it to the material's main texture? Where is your code encountering the problem? For example if you're running this script on the same object that you want the texture to be applied:
this.GetComponent<Renderer>().material.mainTexture = loadedTexture;
Also, when you want to load images, it's best that you use the Resources folder, which uses forwards slashes, not . Store everything that you might want to load on runtime there, and use Resources.Load().