如何在运行时刷新Unity?

3

在这里输入图像描述 在上面的短片中,您可以看到我在Unity中有一个相当简单的设置。有一个计数器,在每一帧(在Update中)递增,旁边是这个调用“UnityEditor.SceneView.RepaintAll()”。您可以看到控制台框中输出了该计数器的值。

一旦我切换到游戏视图并点击左下角的按钮(高亮的按钮按下),它会调用一个脚本,在运行时将网格导入场景并渲染。您会注意到在此期间,尽管很短暂,但非常明显,计数器和重绘调用都会冻结。实际上,在此加载时间期间,场景中的所有内容都会冻结。

我一直在搜索如何刷新Unity场景,以便我可以在运行时加载网格,同时保持玩家的响应性。到目前为止,没有任何结果。

在这里,最佳实践是定期刷新/更新场景,以使玩家不受加载时间游戏停顿的影响?

编辑:RepaintAll() 调用对我无效。我之前应该加上这一点。实际上,这个方法并不像我希望的那样工作,事实上,如果不是为了刷新场景,我也不确定它的目的是什么。

网格导入代码:

    const int MAX_FACETS_PER_MESH = 65535 / 3;

    class Facet
    {
        public Vector3 normal;
        public Vector3 a, b, c;

        public override string ToString()
        {
            return string.Format("{0:F2}: {1:F2}, {2:F2}, {3:F2}", normal, a, b, c);
        }
    }

    private static Mesh[] ImportBinary(string path)
    {
        Facet[] facets;

        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
            {
                // read header
                byte[] header = br.ReadBytes(80);
                uint facetCount = br.ReadUInt32();
                facets = new Facet[facetCount];

                for(uint i = 0; i < facetCount; i++)
                {
                    facets[i] = new Facet();

                    facets[i].normal.x = br.ReadSingle();
                    facets[i].normal.y = br.ReadSingle();
                    facets[i].normal.z = br.ReadSingle();

                    facets[i].a.x = br.ReadSingle();
                    facets[i].a.y = br.ReadSingle();
                    facets[i].a.z = br.ReadSingle();

                    facets[i].b.x = br.ReadSingle();
                    facets[i].b.y = br.ReadSingle();
                    facets[i].b.z = br.ReadSingle();

                    facets[i].c.x = br.ReadSingle();
                    facets[i].c.y = br.ReadSingle();
                    facets[i].c.z = br.ReadSingle();

                    // padding
                    br.ReadUInt16();
                }
            }
        }

        return CreateMeshWithFacets(facets);
    }

    private static Mesh[] CreateMeshWithFacets(IList<Facet> facets)
    {
        int fl = facets.Count, f = 0, mvc = MAX_FACETS_PER_MESH * 3;
        Mesh[] meshes = new Mesh[fl / MAX_FACETS_PER_MESH + 1];

        for(int i = 0; i < meshes.Length; i++)
        {
            int len = System.Math.Min(mvc, (fl - f) * 3);
            Vector3[] v = new Vector3[len];
            Vector3[] n = new Vector3[len];
            int[] t = new int[len];

            for(int it = 0; it < len; it += 3)
            {
                v[it  ] = facets[f].a;
                v[it+1] = facets[f].b;
                v[it+2] = facets[f].c;

                n[it  ] = facets[f].normal;
                n[it+1] = facets[f].normal;
                n[it+2] = facets[f].normal;

                t[it  ] = it;
                t[it+1] = it+1;
                t[it+2] = it+2;

                f++;
            }

            meshes[i] = new Mesh();
            meshes[i].vertices = v;
            meshes[i].normals = n;
            meshes[i].triangles = t;
        }

        return meshes;
    }

请参考此帖子,了解如何使用ThreadPool和我编写的UnityThread类来实现此操作。 - Programmer
@程序员:引用您链接中的“lockstock”的话,“只要在该线程中没有调用任何Unity函数,就可以启动一个单独的线程”。我想使用Mesh构造函数的导入代码(请参见上文)意味着它不能在新线程中运行。 - jtth
@lockstock 这是这种情况吗? - jtth
是的,在另一个线程中不能使用Unity的API,但有一些例外。其中之一是Vector3类,您可以在另一个类中使用它。我不认为您可以使用Mesh类,但可以尝试一下。如果出现错误,则无法使用。导致代码冻结的部分是使用FileStream读取文件的部分。请在另一个线程中执行此操作。 - Programmer
@程序员 不完全确定是否可能。您的意思是将两个“using”语句,FileStreamBinaryReader分开,并单独使用BR吗?不明白这将如何改善整体性能。FS线程需要完全完成才能将其数据发送给BR,不是吗?请澄清。 - jtth
你没有及时回复我的评论,而且你已经有了一个被接受的答案。我的回复将以代码的形式呈现。不要让这个函数返回一个网格,而是让它返回 void。将第一个 using 语句放在一个新线程中。当它完成后,使用我在第一条评论中提供的 API,在主函数中调用 CreateMeshWithFacets。从那里,你可以编写一个返回 Mesh[] 参数的函数。如果你在这个问题上遇到问题或仍需要帮助,你需要创建一个新的问题,并说明你正在做什么以及遇到了什么问题,我会留下答案。 - Programmer
1个回答

5
您正在尝试同步执行文件 IO 操作,这会在它所运行的线程上放置一个锁定。在这种情况下,是主线程。
您可以在剪辑的三个部分中看到此锁定的影响:
- 场景视图不更新 - 游戏视图不更新 - 控制台停止记录日志
这是因为 Update() 根本没有运行。 为了解决这个问题,您需要异步加载文件。

非常清楚易懂,謝謝 Draco。那麼這裡的修復是否像將這些函數變成異步操作一樣簡單呢? - jtth
@jtth 你需要做一些研究。我只在unity里做过一些异步的事情,比如Resources,没有在System.FileIO中尝试过。我进行了一个粗略的搜索,但是只找到了我已经知道的内容。 - Draco18s no longer trusts SE
除了使用(非常简单但卓越出色的)TPL任务之外,我认为在Unity的.Net 3.5运行时中不可用,您将不得不使用单独的线程来进行这样的重型计算。但是,请记住,在WSA应用程序中不支持线程。 - Arshia001

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接