当前位置: 首页 > news >正文

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

http://www.lryc.cn/news/34334.html

相关文章:

  • 设计模式~门面(外观)模式(Facade)-08
  • C++面向对象编程之一:封装
  • IDEA插件系列(3):Maven Helper插件
  • SAP 更改物料基本计量单位
  • 蓝桥web基础知识学习
  • Python+ChatGPT制作一个AI实用百宝箱
  • Python中格式化字符串输出的4种方式
  • C#基础教程15 枚举与类
  • 三步 让你的 vscode 自动编译ts文件
  • STM32程序下载和启动方式
  • 基础01-ajax fetch axios 的区别
  • Android Execution failed for task ‘:app:mergeDebugJavaResource
  • spring事物源码分析
  • 炫龙游戏本Win10系统总是蓝屏崩溃怎么办?
  • 华为OD机试题,用 Java 解【数字加减游戏】问题
  • C++ 手写一个高性能json生成与解析器
  • java——了解反射
  • The Sandbox 中的独特体验——《奥米加》
  • 76 Python写入csv文件时出现空行_newline参数解决
  • 高等数学——定积分和不定积分
  • imx6 usb增强信号强度
  • 深入理解性能压测工具原理
  • Java的概述和运行方式
  • 【C语言】每日刷题 —— 牛客
  • JavaEE课程实践-Servlet的部署(tomcat服务器)
  • Java 中的拆箱和装箱
  • 从0开始自制解释器——实现多位整数的加减法计算器
  • (12)C#传智:File类,泛型,字典,FileStream,StreamReader,多态
  • Dubbo的服务暴漏与服务发现源码详解
  • Python 的IDE——PyCharm