ngui下载序列帧动画用完怎么释放内存

您好,欢迎来到!
[261835]unity3d 一段 序列帧动画播放滑动控制(有参考)
XX天XX时XX分XX秒
赏金分配:单人中标,一人独享赏金。
雇主选一人中标,一人独享中标金!
雇主已托管全部赏金,中标者可得到悬赏金的80%。
现在有一段序列帧动画(数量可以是X帧),需要在unity3d中播放,可以用C# 编写,要求:通过触屏的(ipad或者安卓)左右滑动(或者上下滑动),控制这段序列帧动画的正放和倒放,在不滑动触屏时,画面静止。脚本需要带解释。可以参考一段只可以播放但不能控制的c#代码。
这段代码需要在安卓和IPAD上运行,要求加载图片序列帧时不能有卡顿的现象,参考代码中已经有销毁上一帧的代码了,另外此序列帧动画的实现方法不能采用ngui的atlas方法实现,因为图片数量多,且多为720*1280的。 [补充于
附件:/Myfile//1463.txt
页面加载中...
暂时还没有设计师提交方案
评论加载中...
所在地区:河南-郑州
信用等级:
交稿数量222
分享可赚钱,赶快告诉您的朋友吧Unity+NGUI性能优化方法总结
时间: 12:08:14
&&&& 阅读:2066
&&&& 评论:
&&&& 收藏:0
标签:1 资源分离打包与加载
  游戏中会有很多地方使用同一份资源。比如,有些界面会共用同一份字体、同一张图集,有些场景会共用同一张贴图,有些会怪物使用同一个Animator,等等。可以在制作游戏安装包时将这些公用资源从其它资源中分离出来,单独打包。比如若资源A和B都引用了资源C,则将C分离出来单独打一个bundle。在游戏运行时,如果要加载A,则先加载C;之后如果要加载B,因为C的实例已经在内存,所以只要直接加载B,让B指向C即可。如果打包时不将C从A和B分离出来,那么A的包里会有一份C,B的包里也会有一份C,冗余的C会将安装包撑大;并且在运行时,如果A和B都加载进内存,内存里就会有两个C实例,增大了内存占用。
  资源分离打包与加载是最有效的减小安装包体积与运行时内存占用的手段。一般打包粒度越细,这两个指标就越小;而且当两个renderQueue相邻的DrawCall使用了相同的贴图、材质和shader实例时,这两个DrawCall就可以合并。但打包粒度也并不是越细就越好。如果运行时要同时加载大量小bundle,那么加载速度将会非常慢&&时间都浪费在协程之间的调度和多批次的小I/O上了;而且DrawCall合并不见得会提高性能,有时反而会降低性能,后文会提到。因此需要有策略地控制打包粒度。一般只分离字体和贴图这种体积较大的公用资源。
  可以用AssetDatabase.GetDependencies得知一份资源使用了哪些其它资源。
2 &贴图透明通道分离,压缩格式设为ETC/PVRTC
  最初我们使用了DXT5作为贴图压缩格式,希望能减小贴图的内存占用,但很快发现移动平台的显卡是不支持硬件解压DXT5的。因此对于一张大小的RGBA32贴图,虽然DXT5可将它从4MB压缩到1MB,但系统将它送进显卡之前,会先用CPU在内存里将它解压成4MB的RGBA32格式(软件解压),然后再将这4MB送进显存。于是在这段时间里,这张贴图就占用了5MB内存和4MB显存;而移动平台往往没有独立显存,需要从内存里抠一块作为显存,于是原以为只占1MB内存的贴图实际却占了9MB!
  所有不支持硬件解压的压缩格式都有这个问题。经过一番调研,我们发现安卓上硬件支持最广泛的格式是ETC,苹果上则是PVRTC。但这两种格式都是不带透明(Alpha)通道的。因此我们将每张原始贴图的透明通道都分离了出来,写进另一张贴图的红色通道里。这两张贴图都采用ETC/PVRTC压缩。渲染的时候,将两张贴图都送进显存。同时我们修改了NGUI的shader,在渲染时将第二张贴图的红色通道写到第一张贴图的透明通道里,恢复原来的颜色:
fixed4&frag&(v2f&i)&:&COLOR&&
&&&&fixed4&&&
&&&&col.rgb&=&tex2D(_MainTex,&i.texcoord).&&
&&&&col.a&=&tex2D(_AlphaTex,&i.texcoord).r;&&
&&&&return&col&*&i.&&
  这样,一张4MB的大小的RGBA32原始贴图,会被分离并压缩成两张0.5MB的ETC/PVRTC贴图(我们用的是ETC/PVRTC 4 bits)。它们渲染时的内存占用则是2x0.5+2x0.5=2MB。
3 关闭贴图的读写选项
  Unity中导入的每张贴图都有一个启用可读可写(Read/Write Enabled)的开关,对应的程序参数是。选中贴图后可在Import Setting选项卡中看到这个开关。只有打开这个开关,才可以对贴图使用Texture2D.GetPixel,读取或改写贴图资源的像素,但这就需要系统在内存里保留一份贴图的拷贝,以供CPU访问。一般游戏运行时不会有这样的需求,因此我们对所有贴图都关闭了这个开关,只在编辑中做贴图导入后处理(比如对原始贴图分离透明通道)时打开它。这样,上文提到的大小的贴图,其运行时的2MB内存占用又可以少一半,减小到1MB。
4 减少场景中的GameObject数量
  有一次我们将场景中的GameObject数量减少了近2万个,游戏在iPhone 3S上的内存占用立马减了20MB。这些GameObject虽然基本是在隐藏状态(activeInHierarchy为false),但仍然会占用不少内存。这些GameObject身上还挂载了不少脚本,每个GameObject中的每个脚本都要实例化,又是一比不菲的内存占用。因此后来我们规定场景中的GameObject数量不得超过1万,并且将GameObject数量列为每周版本的性能监测指标。
5&整理图集
  整理图集的主要目的是节省运行时内存(虽然有时也能起到合并DrawCall的作用)。从这个角度讲,显示一个界面时送进显存的图集尺寸之和是越小越好。一般有如下方法可以帮助我们做到这点:
  1)在界面设计上,尽量让美术将控件设计为可以做九宫格拉伸,即UISprite的类型为Sliced。这样美术就可以只切出一张小图,我们在Unity中将它拉大。当然,一个控件做九宫格也就意味着其顶点数量从4个增加到至少16个(九宫格的中心格子采用Tiled做平铺类型的话,顶点数会更多),构建DrawCall的开销会更大(见第6点),但一般只要DrawCall安排合理(同样见第6点)就不会有问题。
  2)同样是在界面设计上,尽量让美术将图案设计成对称的形式。这样切图的时候,美术就可以只切一部分,我们在Unity中将完整的图案拼出来。比如对一个圆形图案,美术可以只切出四分之一;对一张脸,美术可以只切出一半。不过,与第1)点类似,这个方法同样有其它性能代价&&一个图案所对应的顶点数和GameObject数量都增多了。第4点已经提到,GameObject数量的增多有时也会显著占用更多内存。因此一般只对尺寸较大的图案采用这个方法。
  3)确保不要让不必要的贴图素材驻留内存,更不要在渲染时将无关的贴图素材送进显存。为此需要将图集按照界面分开,一般一张图集只放一个界面的素材,一个界面中的UISprite也不要使用别的界面的图集。假设界面A和界面B上都有一个小小的一模一样的金币图标,不要因为在制作时贪图方便,就让界面A的UISprite直接引用界面B中的金币素材;否则界面A显示的时候,会将整个界面B的图集也送进显存,而且只要A还在内存中,B的图集也会驻留内存。对于这种情况,应该在A和B的图集中各放一个一模一样的金币图标,A中的UISprite只使用A的图集,B中的UISprite只使用B的图集。
  不过,如果两个界面之间存在大量相同的素材,那么这两个界面就可以共用同一张图集。这样可以减少所有界面的总内存占用量。具体操作时需要根据美术的设计进行权衡。一般界面之间相同的通用的素材越多,程序的内存负担就越小。但界面之间相同的东西太多的话,美术效果可能就不生动,这是美术和程序之间又一个需要寻求平衡的地方。
  另外,数量庞大的图标资源(如物品图标)不要做在图集里,而应该采用UITexture。
  4)减少图集中的空白地方。图集中完全透明的像素和不透名的像素所占的内存空间其实是一样的。因此在素材量不变的情况下,要尽量减少图集中的空白。有时一张的图集中,素材所占的面积还没超过一半,这时可以考虑将这张图集切成两张512x512的图集。(可能有人会问为什么不能做成一张的图集,这是因为iOS平台似乎要求送进显存的贴图一定是方形。)当然,两张不同图集的DrawCall是无法合并的,但这并不是什么问题(见第6点)。
  应该说,图集的整理在具体操作时并没有一成不变的标准,很多时候需要权衡利弊来最终决定如何整理,因为不管哪种措施都会有别的性能代价。
6&根据各个UI控件的设计安放Panel,隔开DrawCall
  有一次我们发现NGUI的UIPanel.LateUpdate函数的CPU开销非常大。仔细研究之后,发现是合并了太多的DrawCall所致,尤其是将运行时会运动变化的UI控件和静止不变的UI控件的DrawCall合在了一起。当一个UI控件(UIWidget)的位置、大小或颜色等属性发生变化时,UIPanel就需要重建这个控件所用的DrawCall,某些情况下还要重建Panel上的所有DrawCall。有时重建一个DrawCall会消耗不少CPU开销,它需要重新计算这个DrawCall上所有控件的顶点信息,包括顶点位置、UV和颜色等。如果很多控件都集中在同一个DrawCall上,那么只要一个控件有一点点变化,这个DrawCall上的所有控件的顶点就都要重新遍历一边;而我们的UI又大量采用了九宫格拉伸,使控件的顶点数量变得更多,因此重建一个DrawCall的开销就更大。
  因此我们将UI控件分组,将一段时间内会发生变化的控件&&比如怪物头顶的血条和伤害跳字放在同一个Panel上,并且这个Panel上只有这些控件,其余基本不变化的控件就放在别的Panel上。这样两类控件就被隔开到不同的DrawCall不同的Panel中,当一个控件发生变化而导致DrawCall重建时,就不需要遍历那些没有变化的控件。因为在美术设计上,一段时间内在变化的控件总是少数,所以优化效果十分明显,节省的CPU占用率能达到25%。
  这种方法会增加一些DrawCall,但不会有什么影响。我们项目中前期曾经过于重视DrawCall数量的压缩,但后来发现增加几个DrawCall并不是那么可怕的事情。主程有一次甚至用Cocos2d-x做过试验,即使在500个DrawCall的情况下,动画依然可以跑得很流畅,相比之下贴图大小对流畅度的影响要大得多。
7 优化锚点内部逻辑,使其只在必要时更新
  在上一点优化了Panel的DrawCall重建效率之后,我们发现NGUI锚点自身的更新逻辑也会消耗不少CPU开销。即使是在控件静止不动的情况下,控件的锚点也会每帧更新(见UIWidget.OnUpdate函数),而且它的更新是递归式的,使CPU占用率更高。因此我们修改了NGUI的内部代码,使锚点只在必要时更新。一般只在控件初始化和屏幕大小发生变化时更新即可。不过这个优化的代价是控件的顶点位置发生变化的时候(比如控件在运动,或控件大小改变等),上层逻辑需要自己负责更新锚点。
8 降低贴图素材分辨率
  这一招说白了其实就是减小贴图素材的尺寸。比如对一张在原画里尺寸是100x80的贴图,我们将它导入Unity后会把它缩小到50x40,即缩小两倍。游戏实际使用的是缩小后的贴图。不过这一招是必然会显著降低美术品质的,美术立马会发现画面变得更模糊,因此一般不到程序撑不住的时候不会采用。
9 界面的延迟加载和定时卸载策略(暂未实施)
  如果一些界面的重要性较低,并且不常被使用,可以等到界面需要打开显示的时候才从bundle加载资源,并且在关闭时将自己卸载出内存,或者等过一段时间再卸载。不过这个方法有两个代价:一是会影响体验,玩家要求打开界面时,界面的显示会有延迟;二是更容易出bug,上层写逻辑时要考虑异步情况,当程序员要访问一个界面时,这个界面未必会在内存里。因此目前为止我们仍未实施该方案。目前只是进入一个新场景时,卸载上一个场景用到但新场景不会用到的界面。
  以上的9个方法中,4、5、6需要在一定程度上从策划和美术的角度考虑问题,并且需要持续保持监控以维护优化状态(因为在设计上总是会有新界面的需求或改动老界面的需求);其它都是一劳永逸的解决方案,只要实施稳定后,就不需要再在上面花费精力。不过2和8都是会降低美术品质的方法,尤其是8。如果美术对品质的降低程度实在忍不了的话,也可能不会允许采用这两个方法。
后来又学到一招:
不要频繁的使用 &GameObject.SetActive
&我们游戏的某些逻辑会在一帧内频繁调用GameObject.SetActive,显示或隐藏一些对象,数量达到一百多次之多。这类操作的CPU开销很大(尤其是NGUI的UIWidget在激活的时候会做很多初始化工作),而且会触发大量GC。后来我们改变了显示和隐藏对象的方法&&让对象一直保持激活状态(activeInHierarchy为true),而原来的SetActive(false)改为将对象移到屏幕外,SetActive(true)改为将对象移回屏幕内。这样性能就好多了。
使用NGUI时偶尔会遇到以下两个报错:
Supplied NxActorDesc is not valid. createActor returns NULL.
Actor::updateMassFromShapes: Can‘t compute mass from shapes: must have at least one non-trigger shape!
  通过在网上搜索,得知第一个错误其实是PhysX引擎的报错。显然Unity使用了PhysX作为物理引擎。第二个报错说的是无法通过形状计算质量,看来也是物理方面的错误。
  目前这两个错误的具体程序原因未知,但试验发现这和NGUI对象上挂载的BoxCollider形状有关。以下方法可以很容易地重现这两个错误(注意以下步骤都是在编辑状态下,不用播放):
在Unity编辑器中创建一个空场景。
在菜单栏中点击NGUI -& Create -& 2D UI,创建出一个UI Root。
选中刚创建的UI Root,在菜单栏中点击NGUI -& Create -& Panel,在UI Root之下创建出一个Panel孩子。
选中刚创建的Panel,在菜单栏中点击NGUI -& Create -& Widget,在UI Root之下创建出一个Widget孩子。
选中刚创建的Widget,在它身上挂载一个Box Collider。
关键的地方来了:不要勾选Box Collider的Is Trigger属性;设置Box Collider的大小&&x和y都设为正数,但z设为负数;另外将Widget的local scale设为(0, 0, 0)。
选中Panel,将其隐藏(相当于SetActive(false)),这时就会报&Supplied NxActorDesc is not valid. createActor returns NULL.&。
再把Panel打开(相当于SetActive(true)),就会报&Actor::updateMassFromShapes: Can‘t compute mass from shapes: must have at least one non-trigger shape!&。
  如果研究过NGUI内部原理,就会知道NGUI内部会强制给每个Panel挂载一个刚体(Rigidboy)。这是因为在Unity中,没有刚体的碰撞体(Collider)属于静态碰撞体,而静态碰撞体的移动会触发物理引擎中大量的性能开销。(见,里面提到&Colliders can be added to an object without a Rigidbody component to create floors, walls and other motionless elements of a scene. These are referred to as static colliders. In general, you should not reposition static colliders by changing the Transform position since this will impact heavily on the performance of the physics engine.&。)恐怕是刚体和碰撞体之间的交互出了问题。
  一种猜测是:刚体的质量中心是由刚体上各个碰撞体的形状计算出来的(见)。如果碰撞体的形状不正常,那可能就无法计算出质量中心,物理引擎会报错;或者计算出的质量中心就可能是一个非法的数。后一种情况可能还会影响NGUI的触屏检测。有一次我们某个界面上挂载BoxCollider的按钮出bug,点击之后毫无反应,甚至连触摸反馈动画也没有。我本想在UICamera下断点,看看是谁截住了OnClick消息,却发现连UICamera都没感知到触屏。研究了很久才发现那个按钮的BoxCollider的z尺寸设成了负数,调试中我发现那个按钮所在Panel的刚体质量中心是(NaN, NaN, NaN)。将那个BoxCollider的尺寸改成正数后,bug就消失了。(不过后来我也研究过,并不是Box Collider的尺寸为负就一定会导致该bug,可能要正好在某些数值下导致刚体质量中心为(NaN, NaN, NaN)才行。)
  所以目前的结论就是:如果在NGUI中遇到了物理引擎的报错,或者UICamera没感知到触屏,则检查一下各个BoxCollder的尺寸是否正常。这个尺寸会受到多个因素的影响:包括Box Collider的size参数,还有GameObject(包括自己和祖宗)的scale。
原文:http://blog.csdn.net/zzxiang1985/article/details/
&&国之画&&&& &&
版权所有 京ICP备号-2
迷上了代码!25798人阅读
游戏开发(19)
  一共9招。
1 资源分离打包与加载
  游戏中会有很多地方使用同一份资源。比如,有些界面会共用同一份字体、同一张图集,有些场景会共用同一张贴图,有些会怪物使用同一个Animator,等等。可以在制作游戏安装包时将这些公用资源从其它资源中分离出来,单独打包。比如若资源A和B都引用了资源C,则将C分离出来单独打一个bundle。在游戏运行时,如果要加载A,则先加载C;之后如果要加载B,因为C的实例已经在内存,所以只要直接加载B,让B指向C即可。如果打包时不将C从A和B分离出来,那么A的包里会有一份C,B的包里也会有一份C,冗余的C会将安装包撑大;并且在运行时,如果A和B都加载进内存,内存里就会有两个C实例,增大了内存占用。
  资源分离打包与加载是最有效的减小安装包体积与运行时内存占用的手段。一般打包粒度越细,这两个指标就越小;而且当两个renderQueue相邻的DrawCall使用了相同的贴图、材质和shader实例时,这两个DrawCall就可以合并。但打包粒度也并不是越细就越好。如果运行时要同时加载大量小bundle,那么加载速度将会非常慢——时间都浪费在协程之间的调度和多批次的小I/O上了;而且DrawCall合并不见得会提高性能,有时反而会降低性能,后文会提到。因此需要有策略地控制打包粒度。一般只分离字体和贴图这种体积较大的公用资源。
  可以用AssetDatabase.GetDependencies得知一份资源使用了哪些其它资源。
2 &贴图透明通道分离,压缩格式设为ETC/PVRTC
  最初我们使用了DXT5作为贴图压缩格式,希望能减小贴图的内存占用,但很快发现移动平台的显卡是不支持硬件解压DXT5的。因此对于一张大小的RGBA32贴图,虽然DXT5可将它从4MB压缩到1MB,但系统将它送进显卡之前,会先用CPU在内存里将它解压成4MB的RGBA32格式(软件解压),然后再将这4MB送进显存。于是在这段时间里,这张贴图就占用了5MB内存和4MB显存;而移动平台往往没有独立显存,需要从内存里抠一块作为显存,于是原以为只占1MB内存的贴图实际却占了9MB!
  所有不支持硬件解压的压缩格式都有这个问题。经过一番调研,我们发现安卓上硬件支持最广泛的格式是ETC,苹果上则是PVRTC。但这两种格式都是不带透明(Alpha)通道的。因此我们将每张原始贴图的透明通道都分离了出来,写进另一张贴图的红色通道里。这两张贴图都采用ETC/PVRTC压缩。渲染的时候,将两张贴图都送进显存。同时我们修改了NGUI的shader,在渲染时将第二张贴图的红色通道写到第一张贴图的透明通道里,恢复原来的颜色:
fixed4 frag (v2f i) : COLOR
col.rgb = tex2D(_MainTex, i.texcoord).
col.a = tex2D(_AlphaTex, i.texcoord).r;
return col * i.
  这样,一张4MB的大小的RGBA32原始贴图,会被分离并压缩成两张0.5MB的ETC/PVRTC贴图(我们用的是ETC/PVRTC 4 bits)。它们渲染时的内存占用则是2x0.5+2x0.5=2MB。
3 关闭贴图的读写选项
  Unity中导入的每张贴图都有一个启用可读可写(Read/Write Enabled)的开关,对应的程序参数是。选中贴图后可在Import
Setting选项卡中看到这个开关。只有打开这个开关,才可以对贴图使用Texture2D.GetPixel,读取或改写贴图资源的像素,但这就需要系统在内存里保留一份贴图的拷贝,以供CPU访问。一般游戏运行时不会有这样的需求,因此我们对所有贴图都关闭了这个开关,只在编辑中做贴图导入后处理(比如对原始贴图分离透明通道)时打开它。这样,上文提到的大小的贴图,其运行时的2MB内存占用又可以少一半,减小到1MB。
4 减少场景中的GameObject数量
  有一次我们将场景中的GameObject数量减少了近2万个,游戏在iPhone 3S上的内存占用立马减了20MB。这些GameObject虽然基本是在隐藏状态(activeInHierarchy为false),但仍然会占用不少内存。这些GameObject身上还挂载了不少脚本,每个GameObject中的每个脚本都要实例化,又是一比不菲的内存占用。因此后来我们规定场景中的GameObject数量不得超过1万,并且将GameObject数量列为每周版本的性能监测指标。
5&整理图集
  整理图集的主要目的是节省运行时内存(虽然有时也能起到合并DrawCall的作用)。从这个角度讲,显示一个界面时送进显存的图集尺寸之和是越小越好。一般有如下方法可以帮助我们做到这点:
  1)在界面设计上,尽量让美术将控件设计为可以做九宫格拉伸,即UISprite的类型为Sliced。这样美术就可以只切出一张小图,我们在Unity中将它拉大。当然,一个控件做九宫格也就意味着其顶点数量从4个增加到至少16个(九宫格的中心格子采用Tiled做平铺类型的话,顶点数会更多),构建DrawCall的开销会更大(见第6点),但一般只要DrawCall安排合理(同样见第6点)就不会有问题。
  2)同样是在界面设计上,尽量让美术将图案设计成对称的形式。这样切图的时候,美术就可以只切一部分,我们在Unity中将完整的图案拼出来。比如对一个圆形图案,美术可以只切出四分之一;对一张脸,美术可以只切出一半。不过,与第1)点类似,这个方法同样有其它性能代价——一个图案所对应的顶点数和GameObject数量都增多了。第4点已经提到,GameObject数量的增多有时也会显著占用更多内存。因此一般只对尺寸较大的图案采用这个方法。
  3)确保不要让不必要的贴图素材驻留内存,更不要在渲染时将无关的贴图素材送进显存。为此需要将图集按照界面分开,一般一张图集只放一个界面的素材,一个界面中的UISprite也不要使用别的界面的图集。假设界面A和界面B上都有一个小小的一模一样的金币图标,不要因为在制作时贪图方便,就让界面A的UISprite直接引用界面B中的金币素材;否则界面A显示的时候,会将整个界面B的图集也送进显存,而且只要A还在内存中,B的图集也会驻留内存。对于这种情况,应该在A和B的图集中各放一个一模一样的金币图标,A中的UISprite只使用A的图集,B中的UISprite只使用B的图集。
  不过,如果两个界面之间存在大量相同的素材,那么这两个界面就可以共用同一张图集。这样可以减少所有界面的总内存占用量。具体操作时需要根据美术的设计进行权衡。一般界面之间相同的通用的素材越多,程序的内存负担就越小。但界面之间相同的东西太多的话,美术效果可能就不生动,这是美术和程序之间又一个需要寻求平衡的地方。
  另外,数量庞大的图标资源(如物品图标)不要做在图集里,而应该采用UITexture。
  4)减少图集中的空白地方。图集中完全透明的像素和不透名的像素所占的内存空间其实是一样的。因此在素材量不变的情况下,要尽量减少图集中的空白。有时一张的图集中,素材所占的面积还没超过一半,这时可以考虑将这张图集切成两张512x512的图集。(可能有人会问为什么不能做成一张的图集,这是因为iOS平台似乎要求送进显存的贴图一定是方形。)当然,两张不同图集的DrawCall是无法合并的,但这并不是什么问题(见第6点)。
  应该说,图集的整理在具体操作时并没有一成不变的标准,很多时候需要权衡利弊来最终决定如何整理,因为不管哪种措施都会有别的性能代价。
6&根据各个UI控件的设计安放Panel,隔开DrawCall
  有一次我们发现NGUI的UIPanel.LateUpdate函数的CPU开销非常大。仔细研究之后,发现是合并了太多的DrawCall所致,尤其是将运行时会运动变化的UI控件和静止不变的UI控件的DrawCall合在了一起。当一个UI控件(UIWidget)的位置、大小或颜色等属性发生变化时,UIPanel就需要重建这个控件所用的DrawCall,某些情况下还要重建Panel上的所有DrawCall。有时重建一个DrawCall会消耗不少CPU开销,它需要重新计算这个DrawCall上所有控件的顶点信息,包括顶点位置、UV和颜色等。如果很多控件都集中在同一个DrawCall上,那么只要一个控件有一点点变化,这个DrawCall上的所有控件的顶点就都要重新遍历一边;而我们的UI又大量采用了九宫格拉伸,使控件的顶点数量变得更多,因此重建一个DrawCall的开销就更大。
  因此我们将UI控件分组,将一段时间内会发生变化的控件——比如怪物头顶的血条和伤害跳字放在同一个Panel上,并且这个Panel上只有这些控件,其余基本不变化的控件就放在别的Panel上。这样两类控件就被隔开到不同的DrawCall不同的Panel中,当一个控件发生变化而导致DrawCall重建时,就不需要遍历那些没有变化的控件。因为在美术设计上,一段时间内在变化的控件总是少数,所以优化效果十分明显,节省的CPU占用率能达到25%。
  这种方法会增加一些DrawCall,但不会有什么影响。我们项目中前期曾经过于重视DrawCall数量的压缩,但后来发现增加几个DrawCall并不是那么可怕的事情。主程有一次甚至用Cocos2d-x做过试验,即使在500个DrawCall的情况下,动画依然可以跑得很流畅,相比之下贴图大小对流畅度的影响要大得多。
7 优化锚点内部逻辑,使其只在必要时更新
  在上一点优化了Panel的DrawCall重建效率之后,我们发现NGUI锚点自身的更新逻辑也会消耗不少CPU开销。即使是在控件静止不动的情况下,控件的锚点也会每帧更新(见UIWidget.OnUpdate函数),而且它的更新是递归式的,使CPU占用率更高。因此我们修改了NGUI的内部代码,使锚点只在必要时更新。一般只在控件初始化和屏幕大小发生变化时更新即可。不过这个优化的代价是控件的顶点位置发生变化的时候(比如控件在运动,或控件大小改变等),上层逻辑需要自己负责更新锚点。
8 降低贴图素材分辨率
  这一招说白了其实就是减小贴图素材的尺寸。比如对一张在原画里尺寸是100x80的贴图,我们将它导入Unity后会把它缩小到50x40,即缩小两倍。游戏实际使用的是缩小后的贴图。不过这一招是必然会显著降低美术品质的,美术立马会发现画面变得更模糊,因此一般不到程序撑不住的时候不会采用。
9 界面的延迟加载和定时卸载策略(暂未实施)
  如果一些界面的重要性较低,并且不常被使用,可以等到界面需要打开显示的时候才从bundle加载资源,并且在关闭时将自己卸载出内存,或者等过一段时间再卸载。不过这个方法有两个代价:一是会影响体验,玩家要求打开界面时,界面的显示会有延迟;二是更容易出bug,上层写逻辑时要考虑异步情况,当程序员要访问一个界面时,这个界面未必会在内存里。因此目前为止我们仍未实施该方案。目前只是进入一个新场景时,卸载上一个场景用到但新场景不会用到的界面。
  以上的9个方法中,4、5、6需要在一定程度上从策划和美术的角度考虑问题,并且需要持续保持监控以维护优化状态(因为在设计上总是会有新界面的需求或改动老界面的需求);其它都是一劳永逸的解决方案,只要实施稳定后,就不需要再在上面花费精力。不过2和8都是会降低美术品质的方法,尤其是8。如果美术对品质的降低程度实在忍不了的话,也可能不会允许采用这两个方法。
后来又学到一招:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:180909次
积分:2685
积分:2685
排名:第11219名
原创:84篇
评论:157条
(1)(2)(1)(1)(2)(1)(1)(2)(1)(4)(5)(2)(2)(1)(3)(2)(1)(1)(1)(4)(3)(3)(2)(1)(1)(3)(4)(3)(1)(10)(3)(4)(2)(4)(7)}

我要回帖

更多关于 ngui内存释放 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信