从 Application.persistentDataPath 保存和加载数据在 Unity 中有效,但在 iOS 中无效
Save and Load Data from Application.persistentDataPath works in Unity, but not on iOS
我正在构建一个需要将用户创建的数据保存和加载到持久数据路径的应用程序。当我在 Unity Editor 中播放它时它完美运行,但是当我在 iOS 上尝试它时,我得到这个错误:
Uploading Crash Report
NullReferenceException: Object reference not set to an instance of an object.
at CustomModeScript.LoadSavedModes () [0x00000] in <00000000000000000000000000000000>:0
at CustomModeScript.Initialize (System.String fromPanel) [0x00000] in <00000000000000000000000000000000>:0
at MainSettingsPanelScript.OnControlModes () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.Events.UnityAction.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1].Invoke (T1 handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchPress (UnityEngine.EventSystems.PointerEventData pointerEvent, System.Boolean pressed, System.Boolean released) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchEvents () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.Process () [0x00000] in <00000000000000000000000000000000>:0
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
UnityEngine.EventSystems.StandaloneInputModule:Process()
以前是ioexception: Sharing violation on path错误,现在是我重构我的Load函数后的上面的错误。当我尝试加载数据时出现了这个问题(我真的不知道它是否正在保存)
这是保存和加载(检索)的函数
public void SaveModes()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/UserCustomModes.jl");
List<Mode> data = new List<Mode>();
data = savedModes;
bf.Serialize(file , data);
file.Close();
}
// Retrieve saved modes if they exist
public List<Mode> RetrieveSavedModes()
{
if(File.Exists(Application.persistentDataPath + "/UserCustomModes.jl"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/UserCustomModes.jl", FileMode.Open);
List<Mode> storedModesFile = bf.Deserialize(file) as List<Mode>;
savedModes = storedModesFile;
file.Close();
}
return savedModes;
}
调用加载函数的代码如下:
// Populate mode view with saved modes
private void PopulateModeView()
{
ClearCustomModes();
Debug.Log("Loading Modes: " + Manager.savedModes.Count);
if (Manager.savedModes.Count > 0)
{
//Debug.Log("More than 0 modes present");
var index = 0;
foreach (var savedMode in Manager.savedModes)
{
var modeItem = Instantiate(customModeItemPrefab, customModeSectionTransform);
modeItem.GetComponent<CustomModeItemScript>().Initialize(savedMode, index, this, mainSettingsPanelScript);
generatedModeButtons.Add(modeItem);
}
}
}
这是 LoadSavedModes 函数:
private void LoadSavedModes()
{
RemoveAllModeButtons();
Manager.savedModes = Manager.RetrieveSavedModes();
Debug.Log("[UNITY] Retrieve Modes Count: " + Manager.savedModes.Count);
PopulateModeView();
}
提前致谢!
这是我的个人模板 Save/Load
脚本。我知道它适用于函数 GetFilePath
.
当前列出的所有平台
using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections.Generic;
/// <summary>
/// Saves, loads and deletes all data in the game
/// </summary>
/// <typeparam name="T"></typeparam>
public static class SaveLoad<T>
{
/// <summary>
/// Save data to a file (overwrite completely)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="folder"></param>
/// <param name="file"></param>
public static void Save(T data, string folder, string file)
{
// get the data path of this save data
string dataPath = GetFilePath(folder, file);
string jsonData = JsonUtility.ToJson(data, true);
byte[] byteData;
byteData = Encoding.ASCII.GetBytes(jsonData);
// create the file in the path if it doesn't exist
// if the file path or name does not exist, return the default SO
if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(dataPath));
}
// attempt to save here data
try
{
// save datahere
File.WriteAllBytes(dataPath, byteData);
Debug.Log("Save data to: " + dataPath);
}
catch (Exception e)
{
// write out error here
Debug.LogError("Failed to save data to: " + dataPath);
Debug.LogError("Error " + e.Message);
}
}
/// <summary>
/// Load all data at a specified file and folder location
/// </summary>
/// <param name="folder"></param>
/// <param name="file"></param>
/// <returns></returns>
public static T Load(string folder, string file)
{
// get the data path of this save data
string dataPath = GetFilePath(folder, file);
// if the file path or name does not exist, return the default SO
if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
{
Debug.LogWarning("File or path does not exist! " + dataPath);
return default(T);
}
// load in the save data as byte array
byte[] jsonDataAsBytes = null;
try
{
jsonDataAsBytes = File.ReadAllBytes(dataPath);
Debug.Log("<color=green>Loaded all data from: </color>" + dataPath);
}
catch (Exception e)
{
Debug.LogWarning("Failed to load data from: " + dataPath);
Debug.LogWarning("Error: " + e.Message);
return default(T);
}
if (jsonDataAsBytes == null)
return default(T);
// convert the byte array to json
string jsonData;
// convert the byte array to json
jsonData = Encoding.ASCII.GetString(jsonDataAsBytes);
// convert to the specified object type
T returnedData = JsonUtility.FromJson<T>(jsonData);
// return the casted json object to use
return (T)Convert.ChangeType(returnedData, typeof(T));
}
/// <summary>
/// Create file path for where a file is stored on the specific platform given a folder name and file name
/// </summary>
/// <param name="FolderName"></param>
/// <param name="FileName"></param>
/// <returns></returns>
private static string GetFilePath(string FolderName, string FileName = "")
{
string filePath;
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
// mac
filePath = Path.Combine(Application.streamingAssetsPath, ("data/" + FolderName));
if (FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
// windows
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_ANDROID
// android
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_IOS
// ios
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#endif
return filePath;
}
}
这是一个使用脚本的例子
[System.Serializable]
public class TestClass
{
public TestClass()
{
testData = new List<int>();
}
public List<int> testData = new List<int>();
}
public class TestMono : MonoBehaviour
{
private string testFolder = "folder";
private string testFile = "file";
private List<int> myList = new List<int>();
private void Awake()
{
// the ?? is a null comparison so if the value returns null, it generates new data
TestClass loadedData = SaveLoad<TestClass>.Load(folder, file) ?? new TestClass();
myList = loadedData.testData;
}
private void SaveData()
{
TestClass dataToSave = new TestClass
{
testData = myList
};
SaveLoad<TestClass>.Save(dataToSave, folder, file);
}
}
以上代码段未经测试,但它是 Save/Load
脚本的一般用例。
我正在构建一个需要将用户创建的数据保存和加载到持久数据路径的应用程序。当我在 Unity Editor 中播放它时它完美运行,但是当我在 iOS 上尝试它时,我得到这个错误:
Uploading Crash Report
NullReferenceException: Object reference not set to an instance of an object.
at CustomModeScript.LoadSavedModes () [0x00000] in <00000000000000000000000000000000>:0
at CustomModeScript.Initialize (System.String fromPanel) [0x00000] in <00000000000000000000000000000000>:0
at MainSettingsPanelScript.OnControlModes () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.Events.UnityAction.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1].Invoke (T1 handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchPress (UnityEngine.EventSystems.PointerEventData pointerEvent, System.Boolean pressed, System.Boolean released) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchEvents () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.Process () [0x00000] in <00000000000000000000000000000000>:0
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
UnityEngine.EventSystems.StandaloneInputModule:Process()
以前是ioexception: Sharing violation on path错误,现在是我重构我的Load函数后的上面的错误。当我尝试加载数据时出现了这个问题(我真的不知道它是否正在保存)
这是保存和加载(检索)的函数
public void SaveModes()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/UserCustomModes.jl");
List<Mode> data = new List<Mode>();
data = savedModes;
bf.Serialize(file , data);
file.Close();
}
// Retrieve saved modes if they exist
public List<Mode> RetrieveSavedModes()
{
if(File.Exists(Application.persistentDataPath + "/UserCustomModes.jl"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/UserCustomModes.jl", FileMode.Open);
List<Mode> storedModesFile = bf.Deserialize(file) as List<Mode>;
savedModes = storedModesFile;
file.Close();
}
return savedModes;
}
调用加载函数的代码如下:
// Populate mode view with saved modes
private void PopulateModeView()
{
ClearCustomModes();
Debug.Log("Loading Modes: " + Manager.savedModes.Count);
if (Manager.savedModes.Count > 0)
{
//Debug.Log("More than 0 modes present");
var index = 0;
foreach (var savedMode in Manager.savedModes)
{
var modeItem = Instantiate(customModeItemPrefab, customModeSectionTransform);
modeItem.GetComponent<CustomModeItemScript>().Initialize(savedMode, index, this, mainSettingsPanelScript);
generatedModeButtons.Add(modeItem);
}
}
}
这是 LoadSavedModes 函数:
private void LoadSavedModes()
{
RemoveAllModeButtons();
Manager.savedModes = Manager.RetrieveSavedModes();
Debug.Log("[UNITY] Retrieve Modes Count: " + Manager.savedModes.Count);
PopulateModeView();
}
提前致谢!
这是我的个人模板 Save/Load
脚本。我知道它适用于函数 GetFilePath
.
using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections.Generic;
/// <summary>
/// Saves, loads and deletes all data in the game
/// </summary>
/// <typeparam name="T"></typeparam>
public static class SaveLoad<T>
{
/// <summary>
/// Save data to a file (overwrite completely)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="folder"></param>
/// <param name="file"></param>
public static void Save(T data, string folder, string file)
{
// get the data path of this save data
string dataPath = GetFilePath(folder, file);
string jsonData = JsonUtility.ToJson(data, true);
byte[] byteData;
byteData = Encoding.ASCII.GetBytes(jsonData);
// create the file in the path if it doesn't exist
// if the file path or name does not exist, return the default SO
if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(dataPath));
}
// attempt to save here data
try
{
// save datahere
File.WriteAllBytes(dataPath, byteData);
Debug.Log("Save data to: " + dataPath);
}
catch (Exception e)
{
// write out error here
Debug.LogError("Failed to save data to: " + dataPath);
Debug.LogError("Error " + e.Message);
}
}
/// <summary>
/// Load all data at a specified file and folder location
/// </summary>
/// <param name="folder"></param>
/// <param name="file"></param>
/// <returns></returns>
public static T Load(string folder, string file)
{
// get the data path of this save data
string dataPath = GetFilePath(folder, file);
// if the file path or name does not exist, return the default SO
if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
{
Debug.LogWarning("File or path does not exist! " + dataPath);
return default(T);
}
// load in the save data as byte array
byte[] jsonDataAsBytes = null;
try
{
jsonDataAsBytes = File.ReadAllBytes(dataPath);
Debug.Log("<color=green>Loaded all data from: </color>" + dataPath);
}
catch (Exception e)
{
Debug.LogWarning("Failed to load data from: " + dataPath);
Debug.LogWarning("Error: " + e.Message);
return default(T);
}
if (jsonDataAsBytes == null)
return default(T);
// convert the byte array to json
string jsonData;
// convert the byte array to json
jsonData = Encoding.ASCII.GetString(jsonDataAsBytes);
// convert to the specified object type
T returnedData = JsonUtility.FromJson<T>(jsonData);
// return the casted json object to use
return (T)Convert.ChangeType(returnedData, typeof(T));
}
/// <summary>
/// Create file path for where a file is stored on the specific platform given a folder name and file name
/// </summary>
/// <param name="FolderName"></param>
/// <param name="FileName"></param>
/// <returns></returns>
private static string GetFilePath(string FolderName, string FileName = "")
{
string filePath;
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
// mac
filePath = Path.Combine(Application.streamingAssetsPath, ("data/" + FolderName));
if (FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
// windows
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_ANDROID
// android
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_IOS
// ios
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#endif
return filePath;
}
}
这是一个使用脚本的例子
[System.Serializable]
public class TestClass
{
public TestClass()
{
testData = new List<int>();
}
public List<int> testData = new List<int>();
}
public class TestMono : MonoBehaviour
{
private string testFolder = "folder";
private string testFile = "file";
private List<int> myList = new List<int>();
private void Awake()
{
// the ?? is a null comparison so if the value returns null, it generates new data
TestClass loadedData = SaveLoad<TestClass>.Load(folder, file) ?? new TestClass();
myList = loadedData.testData;
}
private void SaveData()
{
TestClass dataToSave = new TestClass
{
testData = myList
};
SaveLoad<TestClass>.Save(dataToSave, folder, file);
}
}
以上代码段未经测试,但它是 Save/Load
脚本的一般用例。