Unity项目优化
发布日期:2021-06-30 19:38:53 浏览次数:3 分类:技术文章

本文共 3635 字,大约阅读时间需要 12 分钟。

以下是整理的一些关于项目优化的点,记录下来:

关于加载效率的优化:

1 简化资源

对于纹理,尽可能将项目中的 RGBA32 和 RGBA16 的贴图转化为 ETC1 纹理进行加载,这样既可以降低 App 和内存的大小,同时也可以极大提高纹理资源的加载速度,进而提高场景的切换速度。同时,对于网格资源,建议对场景中的网格数据进行进一步的压缩,简化网格数据,减少不必要的数据占用。

注:RGBA32、ARGB32 和 RGB24 格式纹理格式较之“硬件支持”格式,会在加载时占据更大的 CPU 耗时,建议尽可能将其转换为两个 ETC1 纹理(RGB  ETC1 纹理  +  Alpha  ETC1 纹理)。

2 尝试分 将部分 UI 资源等常驻内存 

可尝试根据玩家机型的不同而使用不同的加载方式。对于 1G 内存以下的机器(比如红米 1、红米 2 等),使用切换场景卸载上一个场景的资源的策略,但对于 1G 内存以上的设备,可将加载时间较长的UI 资源(即复杂 UI 资源)尽可能常驻内存中,从而避免在场景切换时,同一资源的重复切换。

对象池

频繁的Instantiate 会造成大量的堆内存分配,即使通过 Destroy 销毁实例化的 GameObject,内存中也会驻留大量的内存碎片,从而导致堆内存的快速升高,进而加快系统调用 GC 的频率。而每次 GC 的到来,均会导致系统停止所有进程,并造成大量的 CPU 开销,进而降低游戏运行的流畅度。

少用Resources.UnloadUnusedAssets 

Resources.UnloadUnusedAssets  API 的底层运作机理是,对于每个资源,遍历所有Hierarchy  Tree 中的 GameObject 结点,检测该资源是否被某个 GameObject 所使用,如果全部 GameObject 都没有使用,则引擎才会认定其为 Unused 资源,进而进行卸载操作。因此,该过程极为耗时,并且场景中 GameObject/Asset 数量越高,其开销越大。

可通过 Resources.UnloadAsset 来去除已经确定不再使用的某一资源,对于去除单一资源,该 API 的效率很高,同时也可以降低Resources.UnloadUnusedAssets 统一处理时的压力,进而减少切换场景时该 API 的耗时。

Shader  解析

针对游戏进行时存在较高频率的 Shader.Parse 操作的问题,可通过 Assetbundle 依赖关系打包将 Shader 进行抽离,变成单独含有 Shader 的Assetbundle 文件。这样,可以在游戏启动时,加载该 Assetbundle 并对 Shader 进行统一解析初始化。因为 Shader 自身所占的内存很小,所以建议将 Shader常驻于内存中。 

对于渲染模块的建议: 

对场景和模型的网格进行进一步的简化,从而进一步加快渲染的效率,可以尝试使用 Asset  Store 中的 SimpleLOD 等简化工具来对网格模型进行简化; 

建议根据机型的不同采用不同的渲染 LOD,比如中低端机(红米 Note2 以下)上使用更为简单的材质,从而来降低低端设备的渲染压力

关于UI

对于 UI 元素,Instantiate 操作会带来非常高的 cpu 以及 gc 的开销,因此对于点击频率稍高的界面都通过 GameObject.Activate 和 Deactive 的方式来进行切换。

其次,UI 元素的 OnEnable 和 OnDisable 都会进行较多的操作,因此这种界面切换方式依然会有较大的 CPU 开销,对于点击频率很高的 UI 界面,我们建议更高效的做法是:1.  通过改变 UI 的位置(以 UIPanel 为单位)来实现 UI 的隐藏和显示,因为是位置移动,所以并不产生多余的堆内存和 CPU 消耗,同时又可以节省 Enable 和 Disable 的 CPU 开销。2.通过设置相机的 Culling  mask  来实现  UI  界面的隐藏和显示,同样避免 Enable/Disable 操作。以上做法可能会一定程度地提高内存的开销(UIDrawCall 中存储的拼合 Mesh),因此可以根据UI 切换的频率来决定是否要进行优化。同时需要注意将移出或 cull 掉的 UIPanel 设为 static,并停止所有 UI 元素的动画等,从而避免其带来持续的 CPU 开销。

另, UI 界面的纹理贴图不要开启 Mipmap,对于此类 UI 纹理,开启 Mipmap 并不会加速渲染效率,反而会增加内存占用和加载耗时。

关于UIPanel

尽可能将静态 UI 元素和动态 UI 元素分开,存放于不同的 Panel 下。同时,对于不同频率的动态元素也建议存放于不同的 Panel 中,比如飘血 HUD 和主界面的动态 UI

元素,其更新的频率一般是不同的。对于这种情况,我们建议将两种动态 UI 元素分开存放。

由于 UIPanel 的网格合并是以 Panel 为单位的,因此在一个 UIPanel 中的网格越多,其合并的开销越大,且从经验上看并不是线性的,因此当“飘血”HUD 过多时,我

们建议将“飘血”HUD 放置在不同的 UIPanel 下,每个 UIPanel 中挂 5-­‐10 个 UI 元素,从而避免因为 UI 元素过多而导致 UIPanel.LateUpdate 开销明显增大。

关于FillAllDrawCalls

 NGUI 的网格重建通常比较耗时,其规则为:NGUI 中网格的重建是以 UIPanel 为单位,如果 UI 元素变动,则会导致 UIDrawCall 的变化,进而触发 UIPanel.FillAllDrawCall 这一非常耗时的操作,但如果不会导致 UIDrawCall 的变化(如 UI 元素在不改变深度的情况下的位置移动),那么只会触发 UpdateGeometry 等相对开销不大的操作。

FillAllDrawCalls的调用意味着将一个 UIPanel中的 UI元素进行重排并重新创建 UIDrawCall,同时进行网格的合并,因此,对于比较复杂的 UI 界面,该函数的调用会造成较大的开销。

建议将所以忽隐忽现的 UI 元素从该 UIPanel 中抽离出来,从而避免其 FillAllDrawCalls 的触发。

Rigidbody  数量  &  Collider 

在物理系统方面,影响物理性能的两个重要参数是“Rigidbody”和“Collider”数量。建议尽可能将 Rigidbody  &  Collider 控制在 50 以下。

Contacts  数量 

Contacts 数量表示游戏运行时碰撞对数量的变化,一般来说,碰撞对的数量越多,则物理系统的 CPU 耗时越大。Contacts 的数量一般由场景中碰撞体的数量决定。一般情况下,该碰撞对的产生均由  UI 界面中 UI 元素相互碰撞所导致。

可对 UI 元素进行进检测,查看是否存在大量 UI 元素存在同一深度并相互接触或叠加。

建议对物理碰撞 Layer 进行梳理,通过 Physics  Layer(Edit-­‐>Project  Settings-­‐>Physics 中的 Layer  Collision  Matrix)来过滤部分不需要碰撞检测的 UI 元素。如:一般情况下,UI 元素都在同一层中,如果 UI 元素之间不需要进行碰撞,则在 Layer  Collision  Matrix 中取消该层与自身的碰撞。

资源冗余问题

一般是由三种情况引起,第一种为确实重名资源;第二种为AssetBundle 文件在创建时将同种资源打包到不同 AssetBundle 文件中导致的,第三种为资源加载后并没有完全卸载,使其被存放在某些 Container 中,从而造成了内存中存在多份相同资源的问题。

关于Animation

对于老版本动画系统,尽可能在游戏战斗过程中避免 AddClip  API 的调用。该 API 调用时会在主线程上造成较高的性能开销。一般来说,建议切换场景时使用该 API。

建议使用 Mecanim 动画系统。目前的 Mecanim 动画系统性能要比老版本动画系统高效很多。不仅在底层算法上进行了优化,同时底层还是多线程进行处理。

关于代码堆内存

代码的堆内存分配情况是游戏性能非常重要且经常被忽视的一个环节。代码堆内存分配得过大或过快,都将引起系统底层的垃圾回收机制(GC)来对其进行处理,而每次 GC 的到来,系统均会暂停所有线程的工作,待 GC 完成后再继续工作,因此,每次 GC 的触发均会造成游戏一定程度的卡顿,进而降低游戏体验的流畅感。所以,在项目开发过程中,研发团队应该尽可能减少不必要的堆内存分配。

转载地址:https://linxinfa.blog.csdn.net/article/details/52016169 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Unity3D Shader 入门
下一篇:unity NGUI图文混排

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年04月09日 05时20分57秒