是否存在回收价值类型统一的问题

本文关键字:问题 类型 存在 是否 | 更新日期: 2023-09-27 18:14:51

我发现一篇文章指出,循环使用和重用变量是unity的良好实践。所以我采纳了它。但有一点不清楚:这是否适用于值类型变量(整数,向量)?

是否有这样一个点:

int x;
Vector3 v;
void functionCalledVeryOften(){
   x=SomeCalculation();
   v=SomeCalc();
   //do something with x and v
}

而不是:

void functionCalledVeryOften(){
   int x=SomeCalculation();
   Vector3 v=SomeCalc();
   //do something with x and v
}

是否存在回收价值类型统一的问题

是否有回收值类型统一的意义

是的,有些数据类型不是全部。

这是否适用于值类型变量(整数,向量)?

没有

这取决于变量的类型。

不适用于intdoublefloatboolVector3Vector2等类似的数据类型。它甚至不适用于string,因为string已经不能在c#中重用了。strings是不可变的。

事实上,在局部变量中使用int,比如在while循环中使用int比使用全局变量声明CC_12要快。

*当你应该声明变量一次并重用它的例子,或者用你自己的话来说,在Unity中回收或重用变量*。

:

如果一个函数包含数组,并且该函数经常被调用。

void functionCalledVeryOften()
{
    float[] playerLives = new float[5]; //This is bad because it allocates memory each time it is called
    for (int i = 0; i < playerLives.Length; i++)
    {
        playerLives[i] = UnityEngine.Random.Range(0f,5f);
    }
}

每次分配内存,可以通过将数组设为全局并在函数外部初始化一次来解决。您可以创建一个简单的函数,将数组中的数据重置为0。

float[] playerLives = new float[5]; 
void functionCalledVeryOften()
{
    for (int i = 0; i < playerLives.Length; i++)
    {
        playerLives[i] = UnityEngine.Random.Range(0f,5f);
    }
}

创建新对象:

创建新对象需要资源,并且可能在移动设备上造成问题。这取决于你多久做一次。

下面的代码创建一个GameObject(子弹),然后将Rigidbody附加到它上,然后射击它。当空格键按下时,每帧都会发生这种情况,并在10秒后最终摧毁子弹。

void functionCalledVeryOften()
{
    if (Input.GetKey(KeyCode.Space))
    {
        //Create new Bullet each time
        GameObject myObject = new GameObject("bullet");
        Rigidbody bullet = myObject.AddComponent<Rigidbody>() as Rigidbody;
        //Shoot Bullet
        bullet.velocity = transform.forward * 50;
        Destroy(myObject);
    }
}

上面的代码很糟糕,因为每次创建新的GameObject时它都会分配内存,当GameObject被销毁时,它也会触发垃圾收集器。这可能会减慢你的游戏速度并导致游戏出现问题。

上述代码的解决方案是对象池。你可以在这里了解更多:Object Pooling tutorial from Unity

使用全局变量的简单修复示例:

List<GameObject> reUsableBullets;
int toUseIndex = 0;
void Start()
{
    intitOnce();
}
//Call this function once to create bullets
void intitOnce()
{
    reUsableBullets = new List<GameObject>();
    //Create 20 bullets then store the reference to a global variable for re-usal
    for (int i = 0; i < 20; i++)
    {
        reUsableBullets[i] = new GameObject("bullet");
        reUsableBullets[i].AddComponent<Rigidbody>();
        reUsableBullets[i].SetActive(false);
    }
}
void functionCalledVeryOften()
{
    if (Input.GetKey(KeyCode.Space))
    {
        //Re-use old bullet
        reUsableBullets[toUseIndex].SetActive(true); 
        Rigidbody tempRgb = reUsableBullets[toUseIndex].GetComponent<Rigidbody>();
        tempRgb.velocity = transform.forward * 50;
        toUseIndex++;
        //reset counter
        if (toUseIndex == reUsableBullets.Count - 1)
        {
            toUseIndex = 0;
        }
    }
}
所以基本上,你在Game开始之前在函数中创建一个对象,然后将引用存储在全局变量中。然后您将重用在函数中创建的对象,因为它的引用保存在全局变量中。

:

Instantiate函数用于创建一个预制件的副本。下面的代码将实例化一颗子弹,然后当空格键按下时每帧发射一颗子弹,并在10秒后最终摧毁它。

public GameObject bulletPrefab;
void functionCalledVeryOften()
{
    if (Input.GetKey(KeyCode.Space))
    {
        //Create new Bullet each time
        Rigidbody bullet = Instantiate(bulletPrefab, new Vector3(0, 0, 0), Quaternion.identity) as Rigidbody; 
        //Shoot Bullet
        bullet.velocity = transform.forward * 50;
        Destroy(myObject,10f);
    }
}

上面的代码是糟糕的,因为它分配内存取决于多少组件附加到子弹预制和多少子GameObject是在它。解决方案也是使用对象池。在函数中实例化GameObject,将引用存储在全局变量中,然后重用它们。解决方案与上面的解决方案相同。

总之,你的问题中的示例代码不适用这个

这取决于您希望对该对象做什么。

让我们以第一个例子为例,假设我们想访问变量x &这个函数不需要任何重载来传递变量,并且可以直接访问类实例中的变量。

对于第二个例子,如果我们想做同样的事情。我们必须调用functionCalledEveryOnceSoOften(int, vector3),因为该函数不能直接访问变量。

在unity中,一个函数通常需要使用与另一个函数相同的值,尽管它们可能并不总是在chain中调用。为了在第二个示例中适应这一点,我们必须在函数中添加if语句来过滤掉它。

然而,在第一个示例中,我们可以毫无问题地使用这些变量。这就是经常建议这样做的原因之一。

根据性能,在您的第二个示例中,变量存储在堆栈中而不是堆中,因为它是在方法的范围内定义的,一旦方法结束执行就会被销毁。因此,变量的内存使用并不是一个真正的问题。对于重复的创建和销毁可能会有一个小的开销,但这应该是微不足道的。

在第一个例子中,您将把变量存储在堆上,因为它是在类的作用域中定义的,它只会随着类一起被销毁,并在它的实例化上被创建。这意味着内存可能会在更长的时间内被使用,但不会有创建/销毁变量的开销。这通常也是不重要的。

总之,除非您实例化数千个这样的对象,否则快速连续地访问变量,您很可能不会注意到性能上的很大差异。

最大的区别很可能是代码的编写方式。不管是好是坏。