Sho*_*ulO 5 c# heap optimization stack unity-game-engine
我发现文章说回收和重用变量是团结的好习惯.所以我采纳了它.但有一点不清楚:这是否适用于值类型变量(整数,向量)?
有一点我使用这个:
int x;
Vector3 v;
void functionCalledVeryOften(){
x=SomeCalculation();
v=SomeCalc();
//do something with x and v
}
Run Code Online (Sandbox Code Playgroud)
而不是这个:
void functionCalledVeryOften(){
int x=SomeCalculation();
Vector3 v=SomeCalc();
//do something with x and v
}
Run Code Online (Sandbox Code Playgroud)
回收价值类型是否统一有一点
是的,有些数据类型并非全部.
这适用于值类型变量(整数,向量)吗?
不.
这取决于变量类型.
但这并不适用于int
,double
,float
,bool
,Vector3
以及Vector2
和其他类似的数据类型.它甚至不适用于string
因为已经string
无法在C#中重复使用.strings
是不可改变的.
事实上,使用int
局部变量,让我们说在while
循环中比使用int
声明为全局更快.
*当你要声明变量的例子一次,并重新使用它,或者用自己的话,回收或再利用的变量在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);
}
}
Run Code Online (Sandbox Code Playgroud)
这每次都会分配内存,可以通过使数组全局并在函数外部初始化一次来解决.您可以创建一个简单的函数,将数组中的数据重置为0.
float[] playerLives = new float[5];
void functionCalledVeryOften()
{
for (int i = 0; i < playerLives.Length; i++)
{
playerLives[i] = UnityEngine.Random.Range(0f,5f);
}
}
Run Code Online (Sandbox Code Playgroud)
创建新对象:
创建新对象会占用资源,并可能导致移动设备出现问题.这取决于您执行此操作的频率.
下面的代码创建了一个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);
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码很糟糕,因为每次创建新的GameObject时都会分配内存,当GameObject被销毁时,它也会触发垃圾收集器.这可能会减慢并导致游戏中的打嗝.
上述代码的解决方案是对象池.您可以在此处了解有关它的更多信息:Unity的Object Pooling教程
使用全局变量对此进行简单修复的示例:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以基本上,你在游戏开始之前在函数内创建一个Object,然后将引用存储在一个全局变量中.然后,您将重新使用在函数中创建的对象,因为它的引用保存在全局变量中.
实例化:
该实例化函数用于创建一个预制的副本.下面的代码将实例化一颗子弹,然后每按一次射击它,同时按住空格键,最后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);
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码很糟糕,因为它根据子弹预制件附加的组件数量以及游戏对象下面的子游戏数量来分配内存.解决方案也使用对象池.在函数中实例化GameObject,将引用存储在全局变量中,然后重用它们.解决方案与上述解决方案相同.
总之,您的问题中的示例代码不适用于此.
您可以在此处了解有关Unity中内存管理的更多信息.
这很大程度上取决于您希望对该对象执行的操作。
让我们以第一个例子为例,假设我们想要从第二个函数访问变量 x 和 vfunctionCalledEveryOnceSoOften()
该函数不需要任何重载来传递变量,并且可以直接访问类实例中的变量。
对于第二个例子,如果我们想做同样的事情。我们必须调用functionCalledEveryOnceSoOften(int, vector3)
As 函数无法直接访问变量。
在统一中,通常情况下,一个函数需要使用与另一个函数相同的值,尽管它们可能并不总是在链中调用。为了在第二个示例中适应这一点,我们必须if
在函数内添加语句来过滤掉它。
然而,在您的第一个示例中,我们可以毫无问题地使用这些变量。这是经常建议这样做的原因之一。
根据性能,在第二个示例中,变量存储在堆栈中而不是堆中,因为它是在方法的范围内定义的,一旦方法结束执行,该变量就会被销毁。所以变量的内存使用并不是真正的问题。重复创建和销毁可能会产生少量开销,但这应该是微不足道的。
在第一个示例中,您将变量存储在堆上,因为它是在类的范围内定义的,它只会与类一起销毁,并在实例化时创建。这意味着内存可能会使用更长的时间,但不会有创建/销毁变量的开销。这通常也是微不足道的。
总之,除非您实例化数千个这样的对象,并快速连续地访问变量,否则您很可能不会注意到性能上的很大差异。
最大的区别很可能是代码的编写方式。不管是好是坏。