LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

Unity中支持管理多种对象的对象池

2021/8/2

嘿嘿~

关于对象池

对象池技术是常用的设计模式之一,可以减少因大量创建和销毁实例而消耗的开销。

在unity中使用简单的对象池来管理对象时,我们一般采用队列。在创建对象时出队并将对象setActive(true),在销毁对象时将对象入队并setActive(false)。


选用数据结构

之前写的对象池一般用来管理单种物体,我们希望编写一种对象池来管理多种对象,这样就不用为每种对象都编写各自的对象池。

我们计划让使用者提供预制件,作为参数传递给对象池,达到自动管理的目的。调用时对象池会在池子里面寻找同类物品,若找不到则创建新的物品种类并登记。在简单的对象池中我们使用队列来管理对象,由于此处要管理多种对象,因此我们使用字典嵌套队列Pool,其键为int(预制件的数字id),值为Queue< GameObject >。同时我们也需要一个容器OutObj来记录离开的对象,采用GameObject作为键,int作为值。

关于物品id:正在使用中的每个物品id是唯一的,销毁后id便会失效,但我们使用的预制件本身是不会销毁的,创建的实例都是预制件的clone,因此可以使用。

数据结构定义如下:

public class ObjPool : MonoBehaviour
{
    private Dictionary<int, Queue<GameObject>> ObjPool = new Dictionary<int, Queue<GameObject>>();
    private Dictionary<GameObject, int> OutObj = new Dictionary<GameObject, int>();
}

实现逻辑

编写一个比较复杂的代码块需要许多细节,因此我们先列一个大纲来整理我们的逻辑。

创建物品时:

  1. 创建物体的Create方法需要提供Prefab作为参数,我们把这个预制件使用prefab.GameInstanceID()转化为物品id。
  2. 查询ObjPool有没有这个id的键。
  3. 如果同类物品有可以直接使用的,就取出一个。
  4. 如果没有现成物品,则直接使用预制件实例化一个新的物品。如果这个类型的物品是第一次出现,则在字典添加一个新的元素,键为这个id,值为新的队列。
  5. 在方法返回前,在OutObj对物品做好登记。

销毁物品时:

  1. 先检查销毁的物品是否在OutObj字典中,若不存在,则表明该对象不是从对象池中取出的,发出一个警告。
  2. 把物品从OutObj删除,在ObjPool中添加。

具体实现

public class Objpool : MonoBehaviour
{
    private Dictionary<int, Queue<GameObject>> ObjPool = new Dictionary<int, Queue<GameObject>>();
    private Dictionary<GameObject, int> OutObj = new Dictionary<GameObject, int>();

    public GameObject Create(GameObject prefab)
    {
        int id = prefab.GetInstanceID();
        GameObject obj = _GetFromPool(id);
        if(obj==null)
        {
            obj = Instantiate<GameObject>(prefab);
            if(!ObjPool.ContainsKey(id))
            {
                ObjPool.Add(id, new Queue<GameObject>());
            }
        }

        OutObj.Add(obj, id);
        return obj;
    }


    public void Destory(GameObject obj)
    {
        if(!OutObj.ContainsKey(obj))
        {
            Debug.LogWarning(obj + "并不是对象池创建的!");
            return;
        }

        int id = OutObj[obj];

        obj.transform.parent = transform;
        obj.SetActive(false);

        ObjPool[id].Enqueue(obj);
        OutObj.Remove(obj);

    }

    private GameObject _GetFromPool(int id)
    {
        if(!ObjPool.ContainsKey(id)||ObjPool.Count==0)
        {
            return null;
        }

        GameObject obj = ObjPool[id].Dequeue();
        obj.SetActive(true);
        return obj;
    }

}

作者水平非常有限,也是菜菜子。