Unity实现”对象池管理器“
前言:警告!这可能是坨屎,空闲时间写成,仅作娱乐
在Unity中生成或销毁一个物体会占用较大的资源,如果是制作FPS射击游戏,子弹生成更是雪上加霜。所以我自己写了一个PoolManager,不能和网上的各位大佬作比较,仅作娱乐。
该实例主要是通过一个PoolUtil对象作为一类游戏的对象池,再通过PoolManager来管理诸多PoolUtil。
PoolUtil的功能,主要还是靠队列实现的。调用者每次”销毁“物体时,将物体放入池中,并隐藏起来。再次生成时,查看池中有无待用的物体,若有则直接显示出来。若无,则继续生成新物体。
public class PoolUtil:MonoBehaviour { public GameObject prefab; public string poolName; public Queue<GameObject> queue; /// <summary> /// 初始化方法 /// </summary> /// <param name="prefab">要生成的预制体</param> /// <param name="poolName">池名</param> /// <param name="preLoadCount">预加载的数量</param> public void Init(GameObject prefab,string poolName, int preLoadCount) { queue = new Queue<GameObject>(); this.prefab = prefab; gameObject.name = "PoolUtil_" + poolName; this.poolName = poolName; PreLoad(preLoadCount); } /// <summary> /// 预加载方法 /// </summary> /// <param name="preLoadCount">预加载数量</param> void PreLoad(int preLoadCount) { for (int i = preLoadCount; i > 0; i--) { GameObject newObj = Instantiate(prefab); newObj.SetActive(false); SetGameObject(newObj.transform,GameObject.Find("Canvas").transform); queue.Enqueue(newObj); } } /// <summary> /// 生成方法 /// </summary> /// <param name="pos">生成位置</param> /// <param name="rotation">生成时的旋转度数</param> /// <param name="parent">生成物体的父物体</param> /// <returns>返回生成的物体</returns> public GameObject Spawn(Vector3 pos,Quaternion rotation,Transform parent) { GameObject newObj = null; if (queue.Count!=0) { newObj = queue.Dequeue(); newObj.SetActive(true); SetGameObject(newObj.transform, pos, rotation, parent); return newObj; } newObj = Instantiate(prefab); SetGameObject(newObj.transform, pos, rotation, parent); return newObj; } /// <summary> /// ”销毁“物体 /// </summary> /// <param name="obj">要销毁的目标游戏物体</param> public void DeSpawn(GameObject obj) { obj.SetActive(false); queue.Enqueue(obj); } /// <summary> /// 设置物体的位置旋转和父物体 /// </summary> /// <param name="obj"></param> /// <param name="pos"></param> /// <param name="rotation"></param> /// <param name="parent"></param> private void SetGameObject(Transform obj,Vector3 pos,Quaternion rotation,Transform parent) { obj.SetParent(parent); obj.localRotation = rotation; obj.localPosition = pos; } /// <summary> /// 下面三个是重载 /// </summary> /// <param name="obj"></param> /// <param name="rotation"></param> /// <param name="parent"></param> private void SetGameObject(Transform obj,Quaternion rotation,Transform parent) { obj.SetParent(parent); obj.localRotation = rotation; obj.localPosition = Vector3.zero; } private void SetGameObject(Transform obj,Vector3 pos,Transform parent) { obj.SetParent(parent); obj.localRotation = Quaternion.identity; obj.localPosition = pos; } private void SetGameObject(Transform obj,Transform parent) { obj.SetParent(parent); obj.localRotation = Quaternion.identity; obj.localPosition = Vector3.zero; } }
public class PoolManager : MonoBehaviour { private Dictionary<string, PoolUtil> poolDic; public static PoolManager Instance; private void Start() { //单例模式好调用 Instance = this; DontDestroyOnLoad(gameObject); poolDic = new Dictionary<string, PoolUtil>(); } /// <summary> /// 暴露给调用者的生成方法 /// </summary> /// <param name="needPool">是否需要生成池</param> /// <param name="prefab">要生成的物体</param> /// <param name="pos">位置</param> /// <param name="rotation">旋转</param> /// <param name="parent">父物体</param> public void CreateObj(bool needPool,GameObject prefab,Vector3 pos,Quaternion rotation,Transform parent) { if (needPool)//需要对象池 { QueryPool(prefab).Spawn(pos,rotation,GameObject.Find("Canvas").transform); } else { GameObject temp = Instantiate(prefab); temp.transform.SetParent(parent); temp.transform.localPosition = pos; temp.transform.localRotation = rotation; } } /// <summary> /// 暴露给调用者的“销毁”方法 /// </summary> /// <param name="deleteObj">销毁的目标物体</param> public void DestroyObj(GameObject deleteObj) { QueryPool(deleteObj).DeSpawn(deleteObj); } /// <summary> /// 查询字典中有无该物体的池 /// </summary> /// <param name="prefab">目标物体</param> /// <returns>返回该物体的池</returns> private PoolUtil QueryPool(GameObject prefab) { string key = prefab.name.Split('(')[0]; if (!poolDic.ContainsKey(key)) { CreatePool(prefab); } return poolDic[key]; } /// <summary> /// 创建目标物体的池 /// </summary> /// <param name="prefab">目标物体</param> private void CreatePool(GameObject prefab) { GameObject obj = new GameObject(); PoolUtil util = obj.AddComponent<PoolUtil>(); util.Init(prefab,prefab.name,1); poolDic.Add(prefab.name, util); obj.transform.SetParent(transform); } }
PoolManager主要通过一个池的字典,每次创建物体时,根据调用者的实际情况,判断是否需要创建池?然后加入到字典。这样,调用者再次创建时,即可查找指定池,并将目标物体放入该池中,方便复用。
下面是测试代码
public class TestPool : MonoBehaviour { public GameObject prefab; public void ButtonFunc() { PoolManager.Instance.CreateObj(true,prefab,Vector3.zero,Quaternion.identity,GameObject.Find("Canvas").transform); } }
在Unity新建一个Canvas,将该代码添加到Canvas上。新建一张图片改成红色,然后拖拽到Assets下作为预制体。为测试”销毁“功能,另外新建脚本:
public class ImageFunc : MonoBehaviour { private float timer = 0; private float interval = 5f; void OnEnable() { timer = Time.time; } // Update is called once per frame void Update() { if(Time.time-timer>=interval) { PoolManager.Instance.DestroyObj(this.gameObject); } } }
此代码作为预制体自身的脚本,生成时就开始计时,到达五秒后销毁自己。再次生成时会重置时间为当前时间。
关于该对象池的优化方向:
1.可以从Hierachy面板上点击PoolUtil,观察其Insperctor面板,可查询场景中有哪些物体归这个池管?
2.添加功能销毁池本身,如果长时间不再新生成该对象,应该逐步销毁池中物体,直至池中物体数量归0,然后销毁池
目前就想到这么多了