一、对象池思想

1.使用对象池的好处

很多情况下,我们需要创建大量对象,例如发射子弹,或者大量的AI小兵,不断重新的生成和销毁对象会对性能造成巨大的消耗,所以我们考虑使用对象池技术来管理需大量生成的对象。

2.对象池的组成

对象池的核心包括对象池控制器类(ObjectCtrl)、对象池类(SubPool)以及对象池中具体存放的物品类(obj),当有多个对象时通过SubPool中的list集合进行管理,而当有多个对象池,则通过ObjectCtrl中的Dictionary集合进行管理

ObjectCtrl

ObjectCtrl 好比仓库经理,而SubPool是具体的物品管理员。当你需要使用某件物品时去找仓库经理,仓库经理便通知物品管理员拿给你;当你需要借的物品种类仓库还没有,他则会招聘一个新的物品管理员(RegisterSubPool())。当然,物品的借出和归还你都是和仓库经理进行沟通的,不会直接接触下面的每个物品管理员。

SubPool

SubPool负责具体的物品管理。当物品数量充足时,SubPool只要负责将已有的物品往外拿;当数量不够时,SubPool便会去购置更多物品,即增加对象池总的对象数量。SubPool中存放物品的最大数量等于你同时需要的最大数量,例如场景中同时最多只能产生100个小兵,那么负责管理小兵的对象池中最多就要放100个对象就OK了。

obj类

具体的物品,拿出时需要将各种参数调好以保证正常使用,而归还时需要还原出厂设置便于下次借出。

二、实现代码

1.ReuseObject接口

创建一个接口,用于限定对象池中所保存的对象的基本行为:

public interface IReusable  {
    void Spawn();
    void UnSpawn();
}

对象池中的每个对象需要继承这个接口并实现接口中的方法,Spawn()方法是卵生物体之后所执行的操作,例如小怪属性的初始化;UnSpawn()方法回收物体之后所执行的操作,例如停止导航。

2.SubPool对象池类

对象池中的对象用List进行管理,便于动态扩展。对象池中保存的对象均为隐藏状态,当从对象池中获取对象后,该对象的状态变为显示,同时对象执行1中接口的Spawn()方法完成初始化。使用完毕后归还到对象池,状态变为隐藏,同时执行1中接口的UnSpaw())方法归0所有状态。

public class SubPool  {
//对象集合
List<GameObject> m_pool = new List<GameObject>();
//预设体引用
GameObject m_prefab;
public string Name
{
    get {
        return m_prefab.name;
    }
}
public SubPool(GameObject prefab)
{
    this.m_prefab = prefab;
}
public GameObject Spawn()
{
    GameObject obj = null;
    foreach (GameObject x in m_pool)
    {
        if (!x.activeSelf) {          //如果物体是隐藏的
            obj = x;
            break;
        }
    }

    if (obj == null) {
        obj = GameObject.Instantiate(m_prefab);
        m_pool.Add(obj);
    }

    obj.SetActive(true);              //物体显示出来

    //执行物体的卵生方法
    IReusable reusable = obj.GetComponent<IReusable>();
    if (reusable != null) {
        reusable.Spawn();
    }

    return obj;
}
//回收物体
public void UnSpawn(GameObject go)
{
    if (m_pool.Contains(go))
    {
        //执行物体的放回操作
        IReusable reusable = go.GetComponent<IReusable>();
        if (reusable != null)
        {
            reusable.UnSpawn();
        }
        //隐藏
        go.SetActive(false);
    }
}
//回收所有物体
public void UnSpawnAll()
{
    foreach(GameObject go in m_pool)
    {
        if (go.activeSelf)
        {
            //执行物体的放回操作
            IReusable reusable = go.GetComponent<IReusable>();
            if (reusable != null)
            {
                reusable.UnSpawn();
            }
            //隐藏
            go.SetActive(false);
        }            
    }
}
public bool Contains(GameObject go)
{
    return m_pool.Contains(go);
}
}

3.ObjectCtrl 对象池控制器

如果有很多不同的物体则需要建立多个SubPool对象,当同时存在多个不同的对象池时,需要用控制器进行管理。用Dictionary集合存放所有的对象池,并用对象的名字进行索引。在使用时,即通过对象池控制器生成和释放具体的对象。

public class ObjectPoolCtrl : MonoSingleton<ObjectPoolCtrl> {

private Dictionary<string, SubPool> m_pools = new Dictionary<string, SubPool>();

//从对象池取物体
public GameObject Spawn(string name)
{
    //查找对象池
    SubPool pool;
    if (!m_pools.ContainsKey(name))
    {
        RegisterSubPool(name);
    }
    //取出对应的对象池,并调用Spawn方法
    pool = m_pools[name];
    return pool.Spawn();
}

//注册一个对象池
private void RegisterSubPool(string name)
{
    GameObject prefab = Resources.Load<GameObject>(name);
    SubPool pool = new SubPool(prefab);
    m_pools.Add(pool.Name, pool);
}

public void UnSpawn(GameObject go)
{
    foreach (SubPool pool in m_pools.Values)
    {
        if (pool.Contains(go)) {
            pool.UnSpawn(go);
            break;
        }
    }
}
}