Q&A——性能优化(一)

性能优化

Q1:如图,我们在UI打开或者移动到某处的时候经常会观测到CPU上的冲激,经过进一步观察发现是因为Instantiate产生了大量的GC。想请问下Instantiate是否应该产生GC呢?我们能否通过资源制作上的调整来避免这样的GC呢?如下图,因为一次性产生若干MB的GC在直观感受上还是很可观的。
UWA Tech Doc

准确的说这些 GC Alloc 并不是由Instantiate 直接引起的,而是因为被实例化出来的组件会进行 OnEnable 操作,而在 OnEnable 操作中产生了 GC,比如以上图中的函数为例:

上图中的 Text.OnEnable 是在实例化一个 UI 界面时,UI 中的文本(即 Text 组件)进行了 OnEnable 操作,其中主要是初始化文本网格的信息(每个文字所在的网格顶点,UV,顶点色等等属性),而这些信息都是储存在数组中(即堆内存中),所以文本越多,堆内存开销越大。但这是不可避免的,只能尽量减少出现次数。
因此,我们不建议通过 Instantiate/Destroy 来处理切换频繁的 UI 界面,而是通过 SetActive(true/false),甚至是直接移动 UI 的方式,以避免反复地造成堆内存开销。

性能优化

Q2: 关于场景中玩家和NPC名字的DrawCall的问题。我们项目中是使用TextMesh挂到场景单位上,但是这样每个名字就占了一个DrawCall,请问有没有好的办法优化呢?

游戏中的HUD的做法一般有两种,一种是如上的做法,另一种则是通过NGUI/UGUI来制作HUD。第二种的实现方法大致如下:
(1)计算屏幕中角色在屏幕中的位置;
(2)根据屏幕中的位置来计算各自HUD的位置,并根据HUD的数量分别放置在一个或几个Panel/Canvas下。

第二种方法的优势是尽可能用少的Draw Call数来渲染角色的HUD。开发团队可以就该方法来进行尝试。

性能优化

Q3: 我们能通过Batching来进行效能方面的优化吗?透过粒子发射出来的半透明模型片是无法Saved by Batching ,如果粒子可以使用Dynamic Batching的话,想请教具体的使用规则与方法。

根据Unity 官方的信息,目前粒子系统已经不再进行 Draw Call 的拼合,因为在新版本5.3 中已通过多线程进行更新,暂时无法支持拼合,但性能已经得到提升。
开发团队可以参考:http://forum.Unity3D.com/threads/particle-batching.382835/

性能优化

Q4:请教,角色分部件换装可行吗?比如衣服裤子分开,都是用Skinned Mesh Render,有没有办法合并降低Draw Call?

可以通过合并网格的方式来达到降低Draw Call的效果,具体可查看Asset Store中的换装例子:Character Customization。但是,在角色换装时需要注意以下几点:
(1)装备与角色必须是共用一套骨骼的;
(2)各装备之间所用的材质必须相同。
开发者需要注意,只有同时满足以上两个条件时,才能达到只使用少量Draw Call来进行动态换装的效果。

性能优化

Q5:通过移动位置来隐藏UI界面,会使得被隐藏的UIPanel继续执行更新(LateUpdate有持续开销),那么如果打开的界面比较多,CPU的持续开销是否就会超过一次SetActive所带来的开销?

这确实是需要注意的,通过移动的方式“隐藏”的UI界面只适用于几个切换频率最高的界面,另外,如果“隐藏”的界面持续开销较高,可以考虑只把一部分Disable,这个可能就需要具体看界面的复杂度了。一般来说在没有UI元素变化的情况下,持续的 Update 开销是不太明显的。


性能优化

Q6:游戏中出现UI界面重叠,该怎么处理较好?比如当前有一个全屏显示的UI界面,点其中一个按钮会再起一个全屏界面,并把第一个UI界面盖住。我现在的做法是把被覆盖的界面 SetActive(False),但发现后续 SetActive(True) 的时候会有 GC.Alloc 产生。这种情况下,希望既降低 Batches 又降低 GC Alloc 的话,有什么推荐的方案吗?

可以尝试通过添加一个 Layer 如 OutUI, 且在 Camera 的 Culling Mask 中将其取消勾选(即不渲染该 Layer)。从而在 UI 界面切换时,直接通过修改 Canvas 的 Layer 来实现“隐藏”。但需要注意事件的屏蔽,禁用动态的 UI 元素等等。

这种做法的优点在于切换时基本没有开销,也不会产生多余的 Draw Call,但缺点在于“隐藏时”依然还会有一定的持续开销(通常不太大),而其对应的 Mesh 也会始终存在于内存中(通常也不太大)。

以上的方式可供参考,而性能影响依旧是需要视具体情况而定。

性能优化

Q7:我在Profiler中看到 GC.MarkDependencies 的CPU消耗有900ms+, 虽然在退出战斗的时候调用了Resources.UnloadUnusedAssets(); 可是卡顿还是很明显,请问有什么推荐的方案吗?

GC.MarkDependencies的消耗是由Resources.UnloadUnusedAssets引起的。该函数的主要作用是查找并卸载不再使用的资源。游戏场景越复杂、资源越多,该函数的开销越大,一般在300~2000 ms范围内。所以,我们在UWA报告中加入了对该函数的调用监测,以便让大家更好地掌控它的调用情况。
UWA Tech Doc

对于该函数的优化,我们建议一方面控制场景中不必要的资源量,同时通过UnloadAsset来及时卸载不再使用的资源,以减少Resources.UnloadUnusedAssets的耗时压力。
后续,我们会在加载模块的相关文章中为大家详细讲解Resources.UnloadUnusedAssets的性能问题,敬请期待。

性能优化

Q8: 我在用Profiler真机查看iPhone App时,发现第一次打开某些UI时,Font.CacheFontForText占用时间超过2s,这块主要是由什么影响的?若iPhone5在这个接口消耗2s多,是不是问题很大?这个消耗和已经生成的RenderTexture的大小有关吗?

Font.CacheFontForText主要是指生成动态字体Font Texture的开销, 一次性打开UI界面中的文字越多,其开销越大。如果该项占用时间超过2s,那么确实是挺大的,这个消耗也与已经生成的Font Texture有关系。简单来说,它主要是看目前Font Texture中是否有地方可以容下接下来的文字,如果容不下才会进行一步扩大Font Texture,从而造成了性能开销。


性能优化

Q9:我用Profiler.BeginSample统计到的数据与直接看Memory下的不一样,前者比后者的数据更大,这是为何? 用Profiler的API获取到的这一帧的内存消耗是85.6MB,而堆内存中显示的是62.9MB,这怎么理解?

Profiler.BeginSample
请输入图片描述

Profiler中数据
请输入图片描述

这种情况确实也是经常会遇到的。一帧中分配如此高的内存是会触发GC.Collect的,而Mono中显示的数值则是GC之后的Mono内存数值。

性能优化

Q10:Unity 的 TimeManager Time默认是0.02。我们有个NetConnectionManager脚本使用了FixedUpdate,这个脚本是用来解析网络协议的,我尝试修改Time的值为0.01 ,感觉游戏似乎提高了运行的效率,具体表现在减少了一些卡顿问题的延迟,提高了调用的频率,但是发热和耗电问题似乎会变得严重。关于TimeManager设置,你们有什么建议吗?

将TimeManager中的Time从0.02降低到0.01,其本质是将FixedUpdate的调用频率从默认的50次/秒提升为100次/秒。这一操作确实很可能带来画面表现的流畅感,但同样增加了一倍的网络IO,其本身会占用更大的性能开销,从而造成设备发热。一般来说,除对于网络同步非常高的游戏外,TimeManager中Time按照默认的0.02设置即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值