Unity3d中对象池(ObjectPool)的实现
1 2 | //回收接口。参数是待回收GameObject public static void recycle(GameObject recycleObj); |
1 2 | public static GameObject alloc( string type, float lifetime = 0); public static void recycle(GameObject recycleObj); |
参数lifeTime是存活时间,以秒为单位,定义如下
lifeTime > 0 lifeTime秒后自动回收对象。
lifeTime = 0 不自动回收对象,需游戏主动调用recycle回收。
lifeTime < 0 创建Pool实例并实例化Pool中的对象,但不返回对象,返回值null。
当lifeTime>0时,分配出去的GameObject上挂的PrefabInfo脚本会执行倒计时协程,计时器为0时调用recycle方法回收自己。它的适用对象如射击游戏中的子弹,申请时设定了lifeTime后不必关心回收的问题,当然游戏可以计时器在到时前主动发起回收。
lifeTime < 0的目的预创建对象池,在游戏场景Loading时可以用这个方法先把对象池创建起来,避免游戏中创建对象池造成掉帧。
ObjectPoolMgr用成员poolDic维护已分配的对象池实例
1 | private Dictionary< string , ObjectPool> poolDic = new Dictionary< string , ObjectPool >(); |
使用objectPoolList记录面板上的用户设置
ObjectPoolMgr初始化时会在Unity的层次(Hierarchy)面板中创建GameObject并添加自身脚本。开发者可以在Inspector面板中直接创建新的对象池,如下图。Pre Alloc Size是对象池创建时预申请的对象数量。Auto Increase Size是池中的对象被申请完后进行一定数量的自增。prefab对象池关联的预制类型
1 2 3 4 5 6 7 | public static GameObject alloc( string type, float lifetime = 0){ //根据传入type取出或创建对应类型对象池 ObjectPool subPool = Instance._getpool(type); //从对象池中取一个对象返回 GameObject returnObj = subPool.alloc(lifetime); return returnObj; } |
ObjectPoolMgr会根据传入的类型type,调用_getpool(type)找到对应的Pool,再从其中取一个对象返回。
代码中_getpool(string type)是ObjectPoolMgr中的私有方法。前面说过,ObjectPoolMgr有一个成员poolDic用来记录已创建的对象池实例,_getpool方法先去poolDic中查找,找到直接返回。如果找不到说明还未创建,使用反射创建对象池,记录入poolDic,代码如下
1 2 3 4 5 6 7 8 9 10 | protected Queue queue = new Queue (); //用来保存池中对象 [SerializeField] protected int _freeObjCount = 0; //池中待分配对象数量 public int preAllocCount; //初始化时预分配对象数量 public int autoIncreaseCount; //池中可增加对象数量 protected bool _binit = false ; //是否初始化 [HideInInspector] public GameObject prefab; //prefab引用 [HideInInspector] public string objTypeString; //池中对象描述字符串 |
ObjectPool中的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public virtual GameObject alloc( float lifetime){ //如果没有进行过初始化,先初始化创建池中的对象 if (!_binit){ _init(); _binit = true ; } if (lifetime<0){ Debug.LogWarning( "lifetime <= 0, return null" ); return null ; //lifetime<0时,创建对象池并返回null } GameObject returnObj; if (_freeObjCount > 0){ //池中有待分配对象 returnObj = queue.Dequeue(); //分配 _freeObjCount--; } else { //池中没有对象了,实例化一个 returnObj = Instantiate(prefab , new Vector3(0,0,0), Quaternion.identity) as GameObject; returnObj.SetActive( false ); //防止挂在returnObj上的脚本自动开始执行 returnObj.transform.parent = this .transform; } //使用PrefabInfo脚本保存returnObj的一些信息 PrefabInfo info = returnObj.GetComponent《 PrefabInfo 》(); if (info == null ){ info = returnObj.AddComponent 《 PrefabInfo 》(); } if (lifetime > 0){ info.lifetime = lifetime; } info.types = objTypeString; returnObj.SetActive( true ); return returnObj; } public virtual void recycle(GameObject obj){ //待分配对象已经在对象池中 if (queue.Contains(obj)){ Debug.LogWarning( "the obj " + obj.name + " be recycle twice!" ); return ; } if ( _freeObjCount > preAllocCount + autoIncreaseCount ){ Destroy(obj); //当前池中object数量已满,直接销毁 } else { queue.Enqueue(obj); //入队,并进行reset obj.transform.parent = this .transform; obj.SetActive( false ); _freeObjCount++; } } |
这里要注意的是,基类alloc和recycle方法要使用虚函数,子类override实现多态。
3>对象池子类CubePool
子类override父类的alloc和recycle,进行个性化的申请和回收工作。
1 2 3 4 5 6 7 | public class CubePool : ObjectPool { public override GameObject alloc( float lifetime){ GameObject cubeObject= base .alloc(lifetime); //在这里进行CubePool个性化的的初始化工作 return cubeObject; } } |
当然也可以直接复用基类的alloc方法,甚至不写CubePool类。当ObjectPoolMgr申请一个Cube但找不到CubePool类时,会使用通用方法进行分配和回收。
4>PrefabInfo
PrefabInfo是挂在prefab实例上,用来记录prefab类型和lifetime等数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class PrefabInfo : MonoBehaviour { public string types; [HideInInspector] public float lifetime = 0; void OnEnable(){ if (lifetime > 0){ StartCoroutine(countTime(lifetime)); } } IEnumerator countTime( float lifetime){ yield return new WaitForSeconds(lifetime); ObjectPoolMGR.recycle(gameObject); } } |
新增Pool方法
为一个新Perfab创建对象池需要以下两步,
1、在unity面板中把prefab挂上,并设置prefab的实例化数量和可增加数量
2、(可选)实现一个对应的Pool脚本。如果不实现,这个对象池会使用通用的申请和回收方法。
调用申请和释放方法
1 2 3 4 5 6 | //申请 GameObject obj1 = ObjectPoolMGR.alloc( "Cube" ,5); GameObject obj2 = ObjectPoolMGR.alloc( "Sphare" ); //回收 ObjectPoolMGR.recycle(obj2); |