Unity 代码优化 内存管理优化

项目遇到了卡顿的情况 仔细检查了代码没检查出有误的地方

仔细的总结了一下可以优化的东西
解决了卡顿 记录一下

1 协程

项目之前写的关于倒计时之类的东西 都是开了个协程
虽然协程是消耗很小的线程 , 可是还是有额外消耗
而且 有很多用携程来检测销毁预制体的操作 也都放到Update方法里面解决了
解决方案:放到Update方法里面去执行,协程能不用就不用

还有: 协程的yield 一般都会用到这个 yield return new WaitForSeConds(1f);
如果很多协程 都用 new WaitForSeConds(1f);
那就直接把他变成一个变量 大家公用就行了 这样也不用每次都new一个了
反正只要有new 就会有额外的占用 这是一个容易忽略的地方

2 字符串

百度查到的分析 :

在c#中,字符串是引⽤类型变量⽽不是值类型变量,即使看起来它是存储字符串的值的。这就意味着字符串会造成⼀定的内存垃圾,
由于代码中经常使⽤字符串,所以我们需要对其格外⼩⼼。
c#中的字符串是不可变更的,也就是说其内部的值在创建后是不可被变更的。每次在对字符串进⾏操作的时候(例如运⽤字符串
的“加”操作),unity会新建⼀个字符串⽤来存储新的字符串,使得旧的字符串被废弃,这样就会造成内存垃圾。
我们可以采⽤以下的⼀些⽅法来最⼩化字符串的影响:

减少不必要的字符串的创建,如果⼀个字符串被多次利⽤,我们可以创建并缓存该字符串。
减少不必要的字符串操作,例如如果在Text组件中,有⼀部分字符串需要经常改变,但是其他部分不会,则我们可以将其分为两个部分的组件,对于不变的部分就设置为类似常量字符串即可,比如下⾯的例⼦。

如果我们需要实时的创建字符串,我们可以采⽤StringBuilderClass来代替,StringBuilder专为不需要进⾏内存分配⽽设计,从⽽减少字符串产⽣的内存垃圾。 移除游戏中的Debug.Log()函数的代码,尽管该函数可能输出为空,对该函数的调⽤依然会执⾏,该函数会创建⾄少⼀个字符(空字符)的字符串。如果游戏中有⼤量的该函数的调⽤,这会造成内存垃圾的增加。
在下⾯的代码中,在Update函数中会进⾏⼀个string的操作,这样的操作就会造成不必要的内存垃圾。

正常写一般这样 (内存方面来说 是有问题的)

public Text tx_time; 
float timer; 
void Update() {
    
     
	timer += Time.deltaTime; 
	tx_time.text = "Time:" + timer.ToString(); 
}

解决方案: 把一个Text分成两个Text来拼接
这样写的话 内存没有额外增加 可是代码看起来好蠢 ! 蠢 ! 蠢 !
虽然知道了这样写内存最优 可是我们还是没有采用 先记录一下吧

public Text tx_str; 
public Text tx_time; 
float timer; 
void Start() {
    
     
	tx_str.text = "Time:"; 
} 
void Update() {
    
    
	tx_time.text = timer.ToString() 
}

3 GameObject的Tag优化

比如碰撞方法 有时需要Tag来判断进行一些操作

void OnTriggerEnter2D(Collider2D other)
{
    
     
	if (other.tag == "tag")
            {
    
    
                //操作
            }
}

直接使用gameObject.tag会产生内存垃圾
解决方案:

使用gameObject.CompareTag()

if (other.gameObject.CompareTag(“tag”))
{
    
     
}

4 预制体的创建和销毁

创建
GameObject obj = Instantiate(prefab);
销毁
Destroy( gameObject ) ;

这样是没问题 可是如果很多预制体来频繁创建和消耗 那就是有问题了
于是做了线程池进行优化 , 该销毁时 用隐藏来替代销毁的操作

 public class ObjectPool
    {
    
    
        private static ObjectPool instance;
        private Dictionary<string, Queue<GameObject>> objectPool = new Dictionary<string, Queue<GameObject>>();
        private GameObject pool;
        public static ObjectPool Instance
        {
    
    
            get
            {
    
    
                if (instance == null)
                {
    
    
                    instance = new ObjectPool();
                }
                return instance;
            }
        }
        public GameObject GetObject(GameObject prefab)
        {
    
    
            GameObject _object;

            if (pool == null) //当场景没有对象池时(第一次进入游戏或者切换了场景),新建一个对象池游戏物品并清空字典
            {
    
    
                pool = new GameObject("ObjectPool");
                objectPool = new Dictionary<string, Queue<GameObject>>();
            }

            if (!objectPool.ContainsKey(prefab.name) || objectPool[prefab.name].Count == 0)
            {
    
    
                _object = GameObject.Instantiate(prefab);

                PushObject(_object);

                GameObject childPool = GameObject.Find(prefab.name + "Pool");
                if (!childPool)
                {
    
    
                    childPool = new GameObject(prefab.name + "Pool");
                    childPool.transform.SetParent(pool.transform);
                }

                _object.transform.SetParent(childPool.transform);
            }

            _object = objectPool[prefab.name].Dequeue();
            _object.SetActive(true);
            return _object;

        }
        public void PushObject(GameObject prefab)
        {
    
    
            string _name = prefab.name.Replace("(Clone)", string.Empty);

            prefab.name = _name ;

            if (!objectPool.ContainsKey(_name))
                objectPool.Add(_name, new Queue<GameObject>());

            objectPool[_name].Enqueue(prefab);
            prefab.SetActive(false);

        } 
    }

这样 做到了预制体复用 不用频繁创建销毁 这个优化对项目影响挺大

5 垃圾回收 定时执行GC操作

主动调⽤GC操作 在不影响游戏体验的时候 (场景切换等),我们可以主动的调⽤GC操作:

System.GC.Collect()

以上 是总结的一些优化的地方 , 还请大佬指点一下 谢谢
2023年03月08日19:06:04

猜你喜欢

转载自blog.csdn.net/qq_32065601/article/details/129409431