嘿嘿~
关于对象池
对象池技术是常用的设计模式之一,可以减少因大量创建和销毁实例而消耗的开销。
在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>();
}
实现逻辑
编写一个比较复杂的代码块需要许多细节,因此我们先列一个大纲来整理我们的逻辑。
创建物品时:
- 创建物体的Create方法需要提供Prefab作为参数,我们把这个预制件使用
prefab.GameInstanceID()
转化为物品id。 - 查询ObjPool有没有这个id的键。
- 如果同类物品有可以直接使用的,就取出一个。
- 如果没有现成物品,则直接使用预制件实例化一个新的物品。如果这个类型的物品是第一次出现,则在字典添加一个新的元素,键为这个id,值为新的队列。
- 在方法返回前,在OutObj对物品做好登记。
销毁物品时:
- 先检查销毁的物品是否在OutObj字典中,若不存在,则表明该对象不是从对象池中取出的,发出一个警告。
- 把物品从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;
}
}
作者水平非常有限,也是菜菜子。