Unity移动游戏性能优化:(一)缓存池,回收和预加载
发表于2016-03-11
优化这东西,基本所有游戏都要做,涉及的范围又很多很杂,趁有点时间,赶紧小结小结。
有预感这块可以挖很大的坑,大坑不是一天挖成的,那就从一些基本的概念聊起把。说道缓存池,回收和预加载这几个词,相信即使没做过优化的同学,都会多少了解一些。今天就把他们关系理一理。
一、缓存池:
缓存池是啥?
举个例子,我们的弹幕系统,子弹怎么发? 最傻的方法当然是每次发子弹就Instantiate(),每次子弹不用了,就destroy()。写起来简单方便,但是每个物体在使用时,都需要做开辟内存,拷入数据等等操作。对于子弹这种频繁出现,而每个逻辑高度相似的GameObject就太过浪费了。
这时候就需要缓存池了,简单说来,他就是个容器,存放我们的子弹对象(bullet),当我们需要一个子弹时,就从池中取,位置速度重新初始化一下就可以了。不需要了,再放回池中,disable一下。这样就省去了频繁的内存和初始化操作(当然很多逻辑上的初始化比如reposition还是不可避免的),毕竟,空间换时间是个比较基本的策略么。
具体实现可简单可复杂,简单的就一个List<>。
1、可以参考Unity的官方教程:
http://unity3d.com/cn/learn/tutorials/modules/beginner/live-training-archive/object-pooling
2、复杂点的参考:
http://docs.poolmanager2.path-o-logical.com/code-reference/spawnpool
提供了包括gameobject在场景中的管理,激活关闭,命名等一系列功能。我们游戏的整个缓存系统,就是以这个中间件为基础的(to 造,or not to 造?)
二、回收:
说完了缓存,回收就好理解了。不就是不用的对象放回缓存么,等要用的时候,在拿出来么?
这么简单?打个比方,很多动态生成的对象,飞机吧,一开始是一个只有模型的prefab,然后我们加入了AI,加入了路径,加上了武器,加上了一堆buff... 回收怎么回收?
完整的回收吗?那假如我同一个模型,要不同的路径,再利用的时候,不是有问题?
那我只回收原来的prefab吗?那武器要重用怎么办?又觉得有点浪费啊。
所以这块还是的跟游戏逻辑紧密相连的,需要根据游戏和对象特点建立缓存和回收级别的模型,在根据具体的对象类型,来看回收到哪一层,这样才能比较有效的回收和再利用(我承认,有点抽象,下篇就介绍下我们游戏中抽象出来的层级把)
还有个问题,回收多少个呢?少了不够用,多了内存爆了。那我们总的加个限制把,比如十个。超过10个怎么办?是不回收了?还是清空缓存?还是删一个?
三、预加载:
有缓存和回收了,是不是万事大吉了?显然不是,你只能保证一个对象反复生成的时候不卡,但是这个对象第一次生成的时候呢?实践证明,第一次才是最重要的一次。
第一次要干的事很多,比如,1.要把资源读到内存(极端情况,甚至是要从网上down到本地)这一步,是很慢的。同步做显然是要命的,异步虽然不会卡顿,但是可能更要命。(你打一颗子弹,等1秒钟读了资源在发出)。2.有了资源,要Instantiate出对象,慢。3.接着,对于某些动态生成的对象,还要做包括AddComponent在内的一系列操作,这步当然也是很慢的。4.最后才能初始化逻辑数据。
其中,第一步涉及到io操作,一般是最耗时的(需要补上图吗,暂时不用把)。咋办呢?好办啊,预加载呗。所有游戏进关卡前,都有个loading界面,那就是用来预加载的啊。我们把关卡信息解析一下,把需要的东西读进内存,生成出来,然后丢进缓存池。齐活了。
抛开计量谈毒性都是耍流氓。我们这儿也要谈计量,总不能游戏还没开始,内存就满了把。预加载多少个跟回收多少个是相关的,预加载数量==回收上限==同频出现对象数量, 是比较理想的情况。
我们的游戏里,通过关卡数据可以算出同屏能存在的敌机有多少个(那就加载多少个),然后通过敌机武器类型,发射频率和预估的存货时间算出每种子弹需要多少个,就差不离啦。
下回讲讲我们游戏里针对不同游戏对象的回收和预加载的力度把。
To Be Continued...