Unity移动游戏性能优化:(二)再谈缓存池,缓存池到底能放什么?

发表于2016-03-11
评论2 7.2k浏览

  今天来研究下缓存池在内存有限度的今天,到底能放些什么?在这之前,我们先问两个问题:

1、缓存是要解决什么问题啊?

  缓存是要减小创建GameObject对象的时间消耗。

2、那我们的对象创建到底耗在了哪里呢?

  在Unity下,基本可以概括为4个部分:资源读取,对象创建,动态component的添加,逻辑初始化。

一、资源读取

  假设所有的文件都在本地的话。资源读取就是把prefab的信息(从硬盘)读取到内存中来,有同步和异步两种方式。同步的资源读取,都是非常耗的。

  如下图,没有预加载时,创建一个子弹的profiler截图



  可以看到最耗的GetBulletPrefab中,都是资源读取相关的函数(这个函数调用了Resource.Load)。就是他,严重拖慢比赛进度。

  而这个问题,显然是不能用异步读取来解决的(等三秒在发子弹?)只有预先读取并存储。


二、对象创建:

  资源Load好了,那我们要真正创建对象了。这时,我们一般都是调用Object.Instantiate()来创建对象。这一步也是需要耗时的,如下图,游戏很多资源读取完毕,开始游戏时,大量的调用Instantiate()的消耗:



  最要命的是,资源读取,最多就一次。而对象的创建,却是多次的。在某些极端情况下(满屏弹幕?找策划)大量的Instantiate瞬间就卡住了。

  理论上,Instantiate类似对象构造函数,主要开辟内存填基本数据。按理说,这时候Awake函数等都不会在这时候调?(参考:http://answers.unity3d.com/questions/826877/awake-called-after-i-activate-object-not-after-ins.html 有不同理解的吗?)为什么会耗呢?

  不过这个问题,还是可以通过缓存来解决(预加载加预创建)不过这时候就要算算需要预创建多少个了?


三、动态component的添加:

  同为子弹,都需要有collider,这个可以直接在prefab里做好。但是某些子弹不需要被毁坏,就不用rigidbody(敌人身上有),有些有需要被其他子弹打,所以需要rigidbody。这就导致某些component需要动态的添加。


四、逻辑初始化

  其实逻辑初始化和上一步没有严格的时序关系和区别。经常是在逻辑初始化后,才判断需不需要相应的component的,比如上一步的rigidbody。


五、花费?



  相对较少,优化空间也小。逻辑很多情况下当然也是可以优化的,但是不在这个"缓存池”的讨论范围内。

小结一下比较下个部分特点:

  资源读取:耗时最长,与资源(模型,贴图等)关系紧密,与游戏逻辑关系松散。

  对象创建:耗时较长,与资源关系松散(作为prafab创建时已经不用关心具体是什么资源了),与游戏逻辑有关(需要判断要创建的对象数量)。

  动态component和逻辑初始化:耗时不一定,游戏逻辑密切相关。

好,回到一开始的问题:缓存到底要缓存什么?

1、资源:在内存许可的情况下,尽量预加载所有资源。目的是生成对象的时候,至少不用读取硬盘。

2、对象:在内存许可的情况下,尽量多创建到缓存。目的是每次生成对象的时候,都有闲置的对象直接使用。

3、对象的component:尽量在第一次预加载或者使用时,就将component加上,目的是每次生成对象的时候,不用在addcomponent了。

4、逻辑:看具体逻辑,什么样的数据每次都一样,可以缓存,什么样的数据不行,必须每次初始化呢? 比如子弹,如果要优化到极致,应该是每次发射的时候,速度,威力等等参数尽量不操作,而至改变初始位置。当然这一步可能花费大量开发时间而效果甚微。

  好啦,然后我们把对象分个类,什么对象只用加载资源,什么对象要加载多少个,分清层次,就能实现最小的内存节约最多的时间啦。

  下次有机会在探讨下缓存池的实现和一些实际问题把。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引