首页 > 解决方案 > unity AddComponent 非常低效和缓慢

问题描述

我有一个包含近 120,000 颗星星的数据集和一个包含 215 颗星星的测试数据集(取自主数据集),我正在制作一个程序来读取数据集并在 3D 地球模型周围绘制星星。

  1. 读取数据集并将信息存储在多个列表中(在单例脚本中)
  2. 然后模拟脚本读取列表(销毁单例以提高程序效率):
void listStars()
{
    int i = 0;
    while (i < (StarDataBank.Instance.NumOfStars))
    {
        int primaryID = int.Parse(StarDataBank.Instance.StarIDID[i]);
        string properName = StarDataBank.Instance.StarName[i];
        string HIPID = StarDataBank.Instance.StarIDHIP[i];
        string HDID = StarDataBank.Instance.StarIDHD[i];
        string HRID = StarDataBank.Instance.StarIDHR[i];
        string GLID = StarDataBank.Instance.StarIDGL[i];
        string BFID = StarDataBank.Instance.StarIDBF[i];
        decimal rightAscension = Convert.ToDecimal(StarDataBank.Instance.StarRA[i]);
        decimal declination = Convert.ToDecimal(StarDataBank.Instance.StarDec[i]);
        decimal Mag;
        decimal CI;
        float scale = 0;
        int r = 0;
        int g = 0;
        int b = 0;

        Decimal.TryParse((StarDataBank.Instance.StarMag[i]), out Mag);
        Decimal.TryParse((StarDataBank.Instance.StarCI[i]), out CI);

        if (PlayerPrefs.GetInt("dynamicSize") == 1)
        {
            if (Mag < -1)
            {
                scale = 77.5f;
            }
            else
            {
                if (Mag > -1 && Mag <= 5)
                {
                    scale = 52.5f;
                }
                else
                {
                    if (Mag > 5 && Mag <= 10)
                    {
                        scale = 32.5f;
                    }
                    else
                    {
                        if (Mag > 10 && Mag <= 15)
                        {
                            scale = 17.5f;
                        }
                        else
                        {
                            if (Mag > 15 && Mag <= 20)
                            {
                                scale = 7.5f;
                            }
                            else
                            {
                                if (Mag > 20 && Mag <= 25)
                                {
                                    scale = 2.5f;
                                }
                            }
                        }
                    }
                }
            }
        } 
        else
        {
            scale = 20;
        }
        StartCoroutine(placeStars(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, Mag, CI, scale));
        i++;
    }
    DestroyImmediate(StarDataBank.Instance.gameObject);
}
  1. 每颗星都是用这种方法绘制的:
    IEnumerator placeStars(int primaryID, string properName, string HIPID, string HDID, string HRID, string GLID, string BFID, decimal rightAscension, decimal declination, decimal magnitude, decimal colourIndex, float scale)
    {
        var thisStar = (GameObject)Instantiate(prefabStar, transform.position + getVectors(Convert.ToDecimal(rightAscension), Convert.ToDecimal(declination)), Quaternion.identity);
        thisStar.name = (primaryID).ToString();
        thisStar.transform.parent = StarObject.transform;
        thisStar.transform.localScale = new Vector3(scale, scale, scale);
        thisStar.AddComponent<Star>().newStar(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, magnitude, colourIndex);
        yield return null;
    }

使用只有 215 颗星的较小测试数据集,我能够将 Star 类附加到每个实例化的星。

但是对于具有约 120k 星的大型完整数据集,Unity 编辑器只会在按下播放按钮后挂起。当我注释掉这一行时:thisStar.AddComponent<Star>().newStar(...);程序按预期工作,每个星星都绘制在场景中,唯一的问题是 Star 类没有附加到每个新实例化的星星上。

这意味着 AddComponent 函数不能很好地与我的代码配合使用,尤其是在使用大型数据集时。

有没有更有效的方法将 Star 类附加到每个实例化的 Star GameObject?

另外,在我用完单例后销毁它有什么不同吗?我使用 IEnumerator 实例化有什么不同吗?

编辑:这是明星班:

public class Star : MonoBehaviour
{


    Simulation simulationInstance;


    public int primaryID;  // primary key                                      NEEDS TO BE SET
    public string properName;  // some stars have names                        NEEDS TO BE SET
    public string HIPID;   // ID of star from Hipparcos catalogue              NEEDS TO BE SET
    public string HDID;    // ID of star from Henry Draper catalogue           NEEDS TO BE SET
    public string HRID;    // ID of star from Harvard Revised catalogue        NEEDS TO BE SET
    public string GLID;    // ID of star from Gliese catalogue                 NEEDS TO BE SET
    public string BFID;    // ID of star from BayerFlamsteed catalogue         NEEDS TO BE SET
    public decimal rightAscension; // right ascension of star                  NEEDS TO BE SET
    public decimal declination;    // declination of star                      NEEDS TO BE SET
    public decimal magnitude;  // magnitude of the star                        NEEDS TO BE SET
    public decimal colourIndex;    // colour index of the star                 NEEDS TO BE SET
    public int scale;  // size of the sphere that will represent the star      AUTOMATICALLY SET
    public int red;    // red colour (0-255)                                   AUTOMATICALLY SET
    public int green;  // green colour (0-255)                                 AUTOMATICALLY SET  
    public int blue;   // blue colour (0-255)                                  AUTOMATICALLY SET
    public double x;   //                                                      AUTOMATICALLY SET
    public double y;   //                                                      AUTOMATICALLY SET
    public double z;   //                                                      AUTOMATICALLY SET

    void Start()
    {
        simulationInstance = FindObjectOfType<Simulation>();
    }

    public void newStar(int primaryID, string properName, string HIPID, string HDID, string HRID, string GLID, string BFID, decimal rightAscension, decimal declination, decimal magnitude, decimal colourIndex)
    {
        this.primaryID = primaryID;
        this.properName = properName;
        this.HIPID = HIPID;
        this.HDID = HDID;
        this.HRID = HRID;
        this.GLID = GLID;
        this.BFID = BFID;
        this.rightAscension = rightAscension;
        this.declination = declination;
        this.magnitude = magnitude;
        this.colourIndex = colourIndex;

    }

    public void tellStarInfoPanel()
    {
        simulationInstance.starInfoPanelManager(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, magnitude, colourIndex);
    }
}

编辑:我已将 listStars() 设为协程,将 placeStars() 设为普通方法

    public void placeStars(int primaryID, string properName, string HIPID, string HDID, string HRID, string GLID, string BFID, decimal rightAscension, decimal declination, decimal magnitude, decimal colourIndex, float scale)
    {
        var thisStar = (GameObject)Instantiate(prefabStar, transform.position + getVectors(Convert.ToDecimal(rightAscension), Convert.ToDecimal(declination)), Quaternion.identity);
        thisStar.name = (primaryID).ToString();
        thisStar.transform.parent = StarObject.transform;
        thisStar.transform.localScale = new Vector3(scale, scale, scale);
        thisStar.GetComponent<Star>().newStar(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, magnitude, colourIndex);
        starsRendered++;  
    }

标签: c#classunity3dinstantiationlarge-data

解决方案


有没有更有效的方法将 Star 类附加到每个实例化的 Star GameObject?

是的。您的 Star 预制件应该已经Star 附加了组件。然后您可以将其实例化为var thisStar = (Star)Instantiate(prefabStar, transform.position + getVectors(Convert.ToDecimal(rightAscension), Convert.ToDecimal(declination)), Quaternion.identity);,避免 AddComponent 步骤。

另外,在我用完单例后销毁它有什么不同吗?

不,它不消耗任何处理,您只会释放一些内存。

我使用 IEnumerator 实例化有什么不同吗?

仅当您想在多个帧上实例化对象时,如下所示。

有些事情你可以改进

  1. 在多个帧上实例化星星:为此,您必须将您的listStars方法转换为协程,使返回类型为 IEnumerator 并使用yield return placeStars(primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, Mag, CI, scale). 您可能希望在同一帧上实例化多个星星,或者可能需要一些时间!
  2. 你的 StarStart方法在每一个实例化的星星上变得越来越昂贵,最好将它作为参数传递给placeStars方法,在循环星星之前只获取一次。
  3. 您不需要在每次迭代时都dynamicSize从中获取。PlayerPrefs在循环开始之前存储它。单例实例也是如此。在循环开始之前缓存它并使用缓存的引用。

伪代码示例:

IEnumerator listStars()
{
    var starDataBank = StarDataBank.Instance;
    var dynamicSize = PlayerPrefs.GetInt("dynamicSize");
    var simulationInstance = FindObjectOfType<Simulation>();
    var starsPerFrame = 10;
    for (int i = 0; i < starDataBank.NumOfStars; i++)
    {
        // do your stuff
        for (int j = 0; j < starsPerFrame; j++)
            placeStars(simulationInstance, primaryID, properName, HIPID, HDID, HRID, GLID, BFID, rightAscension, declination, Mag, CI, scale));
        yield return null;
    }
    DestroyImmediate(starDataBank.gameObject);
}

推荐阅读