c# - 从 Application.persistentDataPath 保存和加载数据适用于 Unity,但不适用于 iOS

标签 c# ios unity-game-engine persistent-storage

我正在构建一个应用程序,需要将用户创建的数据保存和加载到持久数据路径。当我在 Unity 编辑器中运行它时,它运行得很好,但是当我在 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:路径共享冲突错误,但现在在我重构我的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();
    }

提前谢谢您!

最佳答案

这是我个人的模板保存/加载脚本。我知道它适用于当前在函数 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);
     }
}

上面的代码片段未经测试,但它是保存/加载脚本的一般用例。

关于c# - 从 Application.persistentDataPath 保存和加载数据适用于 Unity,但不适用于 iOS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69147519/

相关文章:

c# - 触发 SignalR 方法时 HttpContext.Current.Request.Cookies 出现空异常

ios - 如何在 native react 应用程序中安装 facebook sdk?

ios - 使用 Alamofire + SwiftyJSON 无法将数据加载到 UITableView

unity-game-engine - 使用 playscape SDK 1.11 构建游戏时出错

android - Unity 忽略 android 插件 list 文件,导致其 android list 中缺少 <service> 元素

c# - 如何在不锁定文件的情况下将xml反序列化为对象?

c# - 在 C# 中创建一个 dll 的步骤,然后可以从 php 使用

c# - 隐式和显式方法隐藏

objective-c - 桌面 Cocoa 中的 UIGraphicsGetImageFromCurrentImageContext 对应物?

java - 如何在android上运行unity时共享内存?