读取大型 JSON 文件而不挂在 Unity 中

Posted

技术标签:

【中文标题】读取大型 JSON 文件而不挂在 Unity 中【英文标题】:Reading large JSON file without hanging in Unity 【发布时间】:2021-05-29 03:12:47 【问题描述】:

我正在开发一个手机游戏,我需要加载一个大约 17MB 的 4D 60X40X60X40 整数数组。我目前正在读取 JSON 中的数据并使用 SimpleJSON.cs 在 Unity 中反序列化。

使用 SimpleJSON 解析字符串时会出现问题。游戏挂起,加载文件大约需要 30 秒。

我尝试过使用 IJobs,但它们不允许使用字符串(非 blittable)。

主要优先的是悬挂部分。它不允许我在用户等待 JSON 加载时向他们提供反馈。当然,减少加载时间和任何有关它的提示都会非常有帮助。

我的代码:

IEnumerator StartLoadFiles()

    // Wait to give time for the game to load everything else
    yield return new WaitForSeconds(1f);

    /// First read the transition matrices. We are using TextAsset
    /// because of compatibility issues with android.
    TextAsset file;
    yield return file = Resources.Load(transitionPath) as TextAsset;

    loadingSlider.value = 0.3f;
    string transitionString = file.ToString();
    loadingSlider.value = 0.5f;

    /// We are using SimpleJSON.cs as of now. Maybe there is
    /// something faster out there... (Here is where it hangs)
    JSONNode N;
    yield return N = JSON.Parse(transitionString);

    loadingSlider.value = 0.7f;

编辑:我已经在使用协程,没有任何区别。

【问题讨论】:

你可以试试Array Pooling。我不确定这是否会在 4 维数组中有效地工作,但是,如果这对您的情况有任何性能提升,您可以将 4D 数组分离为 4 件 1D 数组。 另外,你可以使用Json.net,它支持直接从流中反序列化。 Deserializing from stream 【参考方案1】:

请注意,协程本身不是异步的!它仍然在 Update 之后的 Unity 主线程中运行 IEnumerator 的每次迭代。

您宁愿使用任务/线程,例如(假设文本资产加载的第一部分按预期工作)

private int[,,,] loadValues = null;


IEnumerator StartLoadFiles()

    // Wait to give time for the game to load everything else
    yield return new WaitForSeconds(1f);

    TextAsset file;
    yield return file = Resources.Load(transitionPath) as TextAsset;

    loadingSlider.value = 0.3f;
    string transitionString = file.ToString();
    loadingSlider.value = 0.5f;

    // Somehow you have to wait for callbacks into the main thread
    // simplest way is a thread-safe Queue
    var actions = new ConcurrentQueue<Action>();

    // Now start the thread and wait for results
    var thread = new Thread(() => LoadingThread(actions));
    thread.Start();

    while(loadValues == null)
    
        // process the actions in the maint hread 
        while(actions.Count > 0 && actions.TryDequeue(out var action)
        
            action?.Invoke();
          

        yield return null;
     

    Debug.Log("Finished loading");

    // Now o something with "loadValues"


private void LoadingThread(ConcurrentQueue actions)

    try
    
        var N = JSON.Parse(transitionString);

        actions.Enqueue(() => loadingSlider.value = 0.7f;);

        var output = new int[60,40,60,40];

        for(var x = 0; x < 60; x++)
        
            for(var y = 0; y < 40; y++)
            
                for(var z = 0; z < 60; z++)
                
                    for(var w = 0; w < 40; w++)
                    
                        // Depending on your json format of course
                        output[x,y,z,w] = N[x][y][z][w];
                    
                
            
          

        actions.Enqueue(() => 
            loadValues = output;
            loadingSlider.value = 1f;
        );
    
    catch(Exception e)
    
        Debug.LogException(e);
        actions.Enqueue(() => loadValues = new int[0,0,0,0];);
    


注意:在智能手机上输入,但我希望思路清晰

【讨论】:

你是真正的上帝,我配不上你。太感谢了!它就像一个魅力,甚至使加载速度快了近 20%。关于如何使加载更快的任何提示? @VaggelisStamkopoulos 不,我不是 ^^ 如果您的文件那么大,Resources.LoadTextAsset.ToString 也相当昂贵。因此,如果有可能通过直接 FileIO 在线程中完全异步加载它们,请改用它!实际上don't use Resources如果有任何其他方式;)例如将您的文件放在StreamingAssets 中并使用流式反序列化.. 这样您甚至不必先将整个内容加载到内存中

以上是关于读取大型 JSON 文件而不挂在 Unity 中的主要内容,如果未能解决你的问题,请参考以下文章

如何逐行读取大型文本文件,而不将其加载到内存中?

Unity粒子系统循环播放代码如何写和调用

运行需要输入而不挂起的命令,但仍然允许输入

unity读取json文件

Streamreader 在读取大型 JSON 文件时无法读取服务器 500 错误中的文件

如何读取具有不同数据类型的大型 JSON 文件