是否存在回收价值类型统一的问题
本文关键字:问题 类型 存在 是否 | 更新日期: 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
}
是否有回收值类型统一的意义
是的,有些数据类型不是全部。
没有这是否适用于值类型变量(整数,向量)?
。
这取决于变量的类型。
不适用于int
、double
、float
、bool
、Vector3
、Vector2
等类似的数据类型。它甚至不适用于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
语句来过滤掉它。
根据性能,在您的第二个示例中,变量存储在堆栈中而不是堆中,因为它是在方法的范围内定义的,一旦方法结束执行就会被销毁。因此,变量的内存使用并不是一个真正的问题。对于重复的创建和销毁可能会有一个小的开销,但这应该是微不足道的。
在第一个例子中,您将把变量存储在堆上,因为它是在类的作用域中定义的,它只会随着类一起被销毁,并在它的实例化上被创建。这意味着内存可能会在更长的时间内被使用,但不会有创建/销毁变量的开销。这通常也是不重要的。
总之,除非您实例化数千个这样的对象,否则快速连续地访问变量,您很可能不会注意到性能上的很大差异。最大的区别很可能是代码的编写方式。不管是好是坏。