# 【性能优化】
# cpu运算优化
1.安全性换性能
.在性能敏感的地方如去掉一些数据安全性的判断,比如分母为0的情况,使用这种方式要格外小心,得确保调用函数的地方数据的正确性
2.空间换时间
.在一些计算量大和反复计算的情景,可以考虑先把一部分的运算结果保存下来,然后计算时可以先判断预算的结果池里是否有运算结果,有就直接拿来用.比如骨骼动画运算结果
.对象池:对于部分常用的数据,避免反复释放和生成消耗大量性能和发热,对象池就很重要了,对象进行反复使用. 比如弹幕游戏里面的子弹
3.时间换空间
.在内存紧张的情况下,游戏会频繁进行gc回收垃圾,这种情况下,就要想办法减少内存的占用了.比如一些纹理贴图,可以使用数学算法生成的方式生成数据,
.时间和空间不可兼得,资源是有限的,只有很好的控制好平衡点,才能使得游戏处于高性能,不发热,内存适中的环境
4.可读性换时间
在一些特殊的情况下,可以牺牲可读性来换取可贵的性能,比如 判断点与圆的距离是否点在圆的范围内,这种情况只需要知道是否在圆内而无需知道实际的距离是多少,那么计算距离时可以不要开根号,而使用一个乘法, 开根号的开销明显比乘法要贵.这么做虽然代码的可理解性变差了,但是性能是较好的.在比较底层的地方,通常是比较稳定不容易出问题的地方,可以采用这种方式
5.分帧处理
在一帧内如果处理的运算量太大,会 导致帧率下降甚至卡顿掉帧情况. 比如在一帧里进行初始化一个模块,会有大量的逻辑,可以尝试把逻辑分摊在连续的多帧执行,在玩家的角度上看,会显得比较流畅
6.多线程处理
比如在发版本工具里,我需要计算文件夹内所有文件里每个文件的md5值,如果使用单线程,会非常的慢,可以使用多线程技术,让多核cpu充分发挥全部运算能力,多线程同时对多文件进行计算,会快很多倍.
7.减少函数层级
在C#里,可以使用函数内联标签,让函数在编译阶段,进行解函数.常用于比较简短的函数.
8.减少遍历次数
根据具体的业务情景,使用合适的遍历方式,尽量使用字典的方式进行储存和读取,需要遍历的情况下,考虑遍历的方向,是顺遍历还是倒遍历还是中间往两边遍历
# 网络加载优化
# 网络通讯
UTP
特点:不可靠但快速
常用于推送一些非重要,但是频繁的数据,比如角色移动数据,等等
TCP
特点:较慢但可靠不丢包
比较常用的方式,一般都用这个通讯协议
# 文件加载
本地文件加载
为了加载不卡顿掉帧,通常使用异步加载的方式.
可通过关卡设计不让玩家走回头路的方式进行回收前面关卡的资源
通过关卡设计比如缓慢攀爬过程掩盖背后默默加载下一关的资源,来实现无缝加载
网络文件加载
如果是通常的游戏常规资源,会先在游戏沙箱盒文件夹或者游戏包内里判断是否有指定需要加载的文件,有的话直接读取本地,否则下载新的文件,且保存于游戏包沙箱内.
# 渲染优化
优化Shader
针对发布平台,判断使用哪种数据类型,比如游戏只针对PC平台,那么统一用 float 的运算数据类型即可,虽然half也可用,但是对于gpu会有多一步把half转换成float的运算,并不好.
纹理:
.使用尽量小的尺寸
.使用Lod技术,自动生成几个不同分辨率的小尺寸纹理,用于应对近,中,远,超远景的不同的细节级别纹理采样
善用顶点颜色
顶点颜色用得好可以用于代替渐变贴图,节省显存和游戏包体积
DrawCall优化
本质就是减少CPU进行调用GPU图形库API的执行次数
因为每次执行图形库API都会对数据进行组合,数据转换,数据缓存等操作
比如进行相同层级的网格和相同Shader的数据进行合并
合批
静态(离线)合批:
牺牲内存,把场景里同材质的静态网格合并成一个模型
动态合批:
对于合并的模型顶点数限制较大,根据顶点,法线,切线的使用情况,仅支持900 300 150 个顶点数
适用于顶点数较少的情况
GPU实例
适用于模型顶点数较多
比如模型摇摆动画,尽量使用shader的顶点偏移的方式来代替Cpu设置坐标的方式进行动画. 在gpu上处理这个动画,性能基本上是微乎其微