原文:http://www.cnblogs.com/chwen/p/4396515.html
前段时间本人转战unity手游,由于作者(Chwen)之前参与端游开发,有些端游的经验可以直接移植到手游,比如项目框架架构、代码设计、部分性能分析,而对于移动终端而言,CPU、内存、显卡甚至电池等硬件因素,以及网络等条件限制,对移动游戏开发的优化带来更大的挑战。
这里就以unity4.5x版本为例,对Unity的优化方案做一个总结,有些是项目遇到的,也有些是看到别人写的不错拿来分享,算作一个整理,后期也会持续更新。本优化从CPU、GPU和内存三个方面着手总结,这一篇先从CPU说起,整理一些针对CPU相关的优化建议。
对CPU的优化主要是从drawcall、物理组件、GC(垃圾回收)、脚本等几个方面开展。
- Drawcall 的优化
- 什么是Drawcall?
Drawcall是CPU向GPU发送绘制命令的接口调用。理论上每一个不同材质的物件需要渲染在屏幕上时,CPU都会调用图形API ( openGL or Diract3D ) 的Draw接口触发显卡进行绘制。
- 为什么优化Drawcall?
Drawcall对硬件和驱动而言,要求大量设置状态(使用哪些顶点、哪些shader等)和状态转换。而Drawcall最大的消耗在于:如果每次drawcall只提交少量的数据将导致CPU瓶颈,CPU无法将GPU填满。Drawcall对GPU的耗费在于硬件一直等待CPU提交数据,而无法得到有效利用。GPU大量的时间耗费在不断切换状态和正确性检测上。 GPU在Draw Call之间,为了防止前后Draw的依赖关系造成绘制错误或者资源竞用,一般会在Draw Call后Flush整个流水线,小粒度的Draw Call对GPU流水线来说是个很大的浪费。(这个问题在D3D老版本存在,在新版D3D11中得到改善。)实际上unity官方指出,Drawcall数量的降低并非重点,重点是减少批次的数量,Drawcall优化实际上是对批次数量的优化。
延伸阅读:
· why are draw calls expensive? — Stack Overflow
· Direct3D Draw函数 异步调用原理解析
- 如何优化Drawcall?
在Unity中对Drawcall的优化有以下几个策略:Drawcall batching,合并打包图集,减少光照和阴影以及遮挡剔除和视锥剔除等。以下分别谈一下各个策略的优缺点。
1.1. Drawcall Batching
Unity中对Drawcall的批次有两种:静态批次(static batching)和动态批次(dynamic batching)。但不论静态批次还是动态批次都要求对象的材质是共享的,即不同材质的对象是无法进行批次的。而且要注意的一点:如果在脚本中调用材质时,使用Renderer.material会造成材质的拷贝,而使用Renderer.sharedMaterial来调用则不会拷贝材质。
1.1.1. 静态批次 Drawcall static batching
场景中的多个物件如果是不移动的(包括位置、缩放、旋转等),并且共享同一材质,比如地形、建筑、花盆等,那么可以选择采用静态批次。静态批次只需要在Inspector勾选static选项即可。静态批次需要注意的是,unity会将进行批次的多个对象合并成一个大的对象,也会导致内存损耗,有时候要避免太多对象静态批次造成的内存过高。这也表明,优化并非绝对做好某一方面,而是平衡各个硬件的瓶颈和效率,选择相对适中的方案。
1.1.2. 动态批次 Drawcall dynamic batching
动态批次是运动的物件在unity中也可以进行批次渲染,动态批次不需要手动设置,是unity自动进行的,但是这里有诸多陷阱和约束,开发者需要遵守一定的限制条件才能享受动态批次的好处。
根据unity官方文档描述:
1) . 动态批次是逐顶点处理的,因此仅对少于900个顶点的mesh有效。如果shader使用了顶点位置,法线和UV那么仅支持低于300顶点的mesh,而如果shader使用了顶点位置,法线、UV0、UV1和切向量,则之多仅支持180顶点。
2) . 缩放问题
缩放对于批次是有影响的,这里涉及到一个统一缩放和非统一缩放的概念。统一缩放即为三轴同比例缩放,比如(1,1,1),(2,2,2)(5,5,5)... 非统一缩放即为三轴不同比例缩放,如(1,2,1)(2,1,1)(1,2,3)等等。
Unity对统一缩放的对象是不进行动态批次的,而对非同一缩放的对象是可以进行动态批次的。这里有点诡异,查阅了一些资料,解释如下:
对于非同一缩放的物件,unity将其mesh进行了复制,因此即便是从相同物件进行的非同一缩放的两个对象是两份mesh;对统一缩放的对象来说,unity不对mesh进行复制,而是使用同一mesh进行缩放,此时复制mesh来进行批次渲染是不值得的,但是对于非统一缩放的对象,既然已经复制了mesh(不是为了批次,而是其他原因决定复制mesh),那么进行批次是顺带实现的。
(参考 Dynamic Batching and Scale ——unity3d answers )
3) . 使用了不同的材质,即便实质上是相同的(比如两个一模一样的材质),也不会进行批次。
4) . 拥有lightmap的物件含有额外的材质属性,比如lightmap偏移和缩放系数等,所以拥有lightmap的物件不能批次。
5) . 多通道的shader会妨碍批处理操作,接受实时阴影的物件无法批次。
注意: unity渲染是有顺序的,渲染排序有可能打断动态批次。
例如:
场景中有物件ABC,假设AB使用同一材质1,C使用材质2.那么drawcall有可能是2个,也有可能是3个。
如果顺序为:
1.渲染A,使用材质1
2.渲染B,使用材质1
3.渲染C,使用材质2
那么drawcall是2个,AB进行了动态批次。
如果顺序为:
1.渲染A,使用材质1
2.渲染C,使用材质2
3.渲染B,使用材质1
那么drawcall就是3个,AB的批次被C打断了。
渲染顺序跟什么有关呢?
首先根据物件到摄像机的距离,进行远处物件先渲染近处物件后渲染。相同材质的物件尽量在一层,不要让不同材质的物件进入这一层。如果无法保证这一点,那么还有一种方法:修改shader中渲染队列值。即打开shader 将subshader中的tag{}中queue 修改为小于2500的值。
渲染队列小于等于2500时,unity认为其是不透明的,对于不同材质但z值相同对象,unity不对其进行排序,这样能保证相同材质的多个对象能是一个批次,不同材质的对象如果进入两个相同材质的对象之间,不会打破批次;
渲染队列大于2500时,unity会对不同材质的对象进行排序,此时如果不同材质的对象进入到两个相同材质的对象之间的话,会使相同材质的对象批次被打破。
批次先写到这,其实很多网上都有,不过有些没深入讲解,也有些没给出解决办法,我就使用每个方案时遇到的困难给出了自己的解决方案。其实批次还有不少研究的地方,之后想到了会继续更新。
延伸阅读:
1.2. 合并图集
其实合并图集也是利用了Unity的Drawcall batching。将多个纹理进行打包成图集是为了减少材质,这样多个对象共享一个材质,并进而使用同一个纹理和shader,触发unity的动态批次。图集打包工具有很多,Asset store中也可以搜到不少,比如Texture Packer Free 、 DrawCall Optimizer(收费) Mesh Baker Free 等等都可以将贴图打包合并。
但是合并图集也有缺点,合并贴图时应该注意选择同时出现在屏幕的对象贴图进行合并。如果不能做到这一点,那么合并图集可能起到反作用,即渲染一个对象需要加载过多无用贴图,造成内存占用率升高。我的项目这个方案也是采用之后又弃用的,因为归类同时出现在屏幕的贴图并非易事!
1.3. 光照和阴影
实时光照和阴影可能增加Drawcall,带有光源计算的shader材质会因为光照产生多个Drawcall。使用灯光会打断Drawcall batching,尽量使用烘焙灯光贴图等技巧来实现灯光效果。
延伸阅读:
· Forward Rendering Path Details
· Light Troubleshooting and Performance
1.4. 遮挡剔除、视锥剔除
这两个Unity提供的剔除方案,出视野之后应剔除对象渲染。
2. 物理组件
3. GC
GC是unity自动回收内存垃圾的回收器,这虽没有内存泄漏的风险,但是过多的垃圾回收会让CPU高负荷。这里就要避免不必要的内存申请和释放。可以在某个脚本定时清理垃圾,如void update(){if(Time.framecount %5 ==0)System.GC.collect();}
有以下几点需要注意:
3.1. 字符串的拼接会产生临时字符串内存,移除代码中的字符串拼接,改用string.format,或stringbuilder,这没测。
3.2. 用for代替foreach,foreach每次迭代产生24字节垃圾内存。100次循环就是2.4kB.
3.3. 对象标签tag比较采用comparetag,不要用tag=="mytag"这样。
3.4. 使用对象池。对象克隆也是调用new,因此对于可以循环利用的对象要采用对象池,比如子弹、特效、宝石等等。
对象池使用时要注意一点:如果对象上挂了脚本,那么数据需要每次进行初始化或对象回收的时候进行重置,否则下次再利用的对象脚本可能存留上次的数据,那极有可能出bug。
3.5. 尽量使用struct而非class,因为struct是栈区,class是堆区。
GC涉及到内存,详细内容会在内存篇展开。
4. 脚本
脚本中如果在update 函数中调用了Getcomponent等接口,最好将组件缓存。
在update中的处理如果允许,尽量隔多帧处理一次。
如:将update() {DoSth();} 修改为:update() { if(Time.framecount %5 == 0) DoSth();}
其实脚本的优化主要就是针对update中复杂和耗费的逻辑进行优化,其次对于游戏进行中的卡顿,可以修改逻辑,使用预加载方式,将游戏进行中的对象在开始前一次性加载到内存。
Unity官方给出的一些优化建议:
相关推荐
Unity性能优化是游戏开发中的重要环节,特别是在使用Unity3D引擎进行C#编程时,确保游戏运行流畅、资源高效利用是提升用户体验的关键。本PDF文档很可能是针对Unity引擎的性能优化策略进行深入探讨的资料,涵盖了多...
Unity性能优化是指在游戏开发中提高游戏性能的过程,包括优化CPU和GPU的使用,以提高游戏的帧率和画质。下面是 Unity性能优化的相关知识点: 1. Unity性能调试工具及使用方法 Unity提供了多种性能调试工具,例如...
《Unity性能优化——官方白皮书/手册 2021版》是Unity Technologies发布的针对游戏开发者的重要参考资料,旨在帮助开发者提升游戏运行效率,优化资源消耗,提高用户体验。本手册详细阐述了Unity引擎在2021年推荐的...
Unity中的性能优化CPU优化 减少Draw Calls 提高代码效率 顶点优化 优化几何体 使用LOD(Level of detail) 使用遮挡剔除(Occlusion culling) 像素优化 控制绘制顺序 减少实时光照 带宽优化
1. 利用Unity内置的Profiler工具进行性能分析,找出瓶颈并针对性优化。 2. 使用Profiler的CPU和GPU采样,分析Draw Call、内存使用和脚本执行时间。 七、移动设备优化 1. 考虑移动设备的硬件限制,如内存和处理器...
在Unity3D游戏开发中,性能优化是至关重要的,它直接影响到游戏的运行效率和玩家体验。本篇文章将深入探讨两个关键的生命周期方法——`OnBecameVisible`和`OnBecameInvisible`,以及如何利用它们来提升游戏性能。 `...
总之,在Unity项目开发过程中,性能优化需要系统地进行,从美术资源的规范导入,到脚本编写时的CPU时间管理,再到内存使用和GPU性能的优化,都需要开发团队的共同关注。同时,合理运用性能分析工具,及时发现和解决...
《Unity + NGUI 性能优化方法总结》 在Unity引擎中,特别是在使用NGUI(Unity的用户界面系统)时,性能优化是一项至关重要的任务。本文将总结九个关键的性能优化方法,帮助开发者实现更高效的游戏体验。 1. 资源...
在Unity 2017环境下,性能优化不仅涉及对硬件资源(如CPU、GPU、内存和存储)的有效利用,还包括确保游戏在各种设备上都有良好的运行表现。 知识点二:Unity 2017的性能优化工具和策略 Unity 2017游戏优化涉及到一...
Unity3D性能优化的渲染篇主要关注如何提升游戏在GPU层面的运行效率,因为渲染是图形处理的关键环节,直接影响游戏的帧率和整体性能。在Unity中,每一帧的渲染涉及CPU和GPU的协同工作,具体流程包括物体检查、渲染...
### Unity性能优化(2021版)知识点详解 #### 一、Unity项目优化概念 Unity项目优化主要是针对游戏开发过程中出现的各种性能问题进行解决,包括但不限于提高游戏运行效率、减少资源消耗、优化视觉表现等方面的工作...
本文将深入探讨Unity引擎在处理大型场景时所面临的问题以及相应的优化策略,帮助开发者提升游戏性能,降低资源消耗。 一、场景管理优化 1. 分区管理:大场景可以被划分为多个小区域,每个区域加载和卸载独立,减少...
### Unity性能优化系列之一 – 脚本优化 在Unity引擎开发过程中,脚本优化是提升游戏性能的关键一环。良好的脚本优化不仅能减少不必要的CPU消耗,还能提高整体的运行效率,为玩家带来更加流畅的游戏体验。本文将...
整体来看,Unity优化涉及到了对游戏开发过程中各种资源的有效管理,从图形渲染优化到角色模型优化,再到资源的合理使用,这些细节的优化将直接影响游戏的性能和最终用户的体验。通过对Unity优化知识的学习和实践,...
本文将围绕Unity中的优化技术展开讨论,涵盖影响游戏性能的因素及其解决方案,并通过具体的技术手段帮助开发者提高游戏的性能表现。 #### 影响性能的关键因素 理解游戏性能的瓶颈是进行有效优化的前提。对于Unity...
Unity 场景优化是指在 Unity 游戏引擎中对场景进行优化,以提高游戏的性能和效率。优化的目标是减少 DrawCall 的次数、减少 CPU 的负载、提高渲染效率等。 在 Unity 中,DrawCall 是指 CPU 将数据发送给 GPU 进行...
10. **监控Profiler**:Unity的Profiler工具是性能优化的关键,通过分析Draw Call、CPU使用率和内存分配等数据,可以定位性能瓶颈并进行针对性优化。 `UGUI_BatchDemo`示例工程应该包含了一系列的场景和脚本,演示...
"Unity优化工具"正是针对这一需求而设计的资源包,它包含了帮助开发者提升游戏性能的各种工具和教程。 文档中可能涵盖了以下关键知识点: 1. **内存管理**:讲解如何有效地管理Unity中的内存,包括减少场景加载时...
总之,Unity3D的资源优化是一个持续的过程,开发者需要结合实际项目需求,灵活运用各种优化手段,确保游戏的性能和体验。《unity大场景优化.pdf》作为专门的参考资料,无疑是提升这方面的知识和技能的重要资源。
这篇文档主要探讨了如何通过优化各种技术细节来提升游戏性能,确保在移动设备上流畅运行。以下是一些重要的优化策略和知识点: 1. **纹理优化**: - **纹理压缩**:Unity支持多种纹理压缩格式,如ETC2、ASTC等,...