Unity 对象池

       频繁创建和销毁对象会造成性能的开销。 创建对象的时候,系统会为这个对象开辟一片新的空间。销毁对象的时候,这个对象会变成内存垃圾,当内存垃圾达到一定程度,就会触发垃圾回收机制,清理内存垃圾,由于此时在清理垃圾,所以程序有可能就会变卡。 为了改善这个问题,我们就可以使用对象池。使用了它之后,程序的性能就能得到提升不那么容易变卡。 对象池的原理:

 1、当要创建对象的时候,不直接创建,而是先从对象池里面找,如果对象池里有这种类型的对象,就从对象池中取出来用。如果对象池里面没有这种类型的对象,才创建该对象。

  2、当要销毁对象的时候,不直接销毁,而是把这个对象放进对象池里面存着,以便下一次创建对象时使用。

using System.Collections;

using System.Collections.Generic;

using UnityEngine; /// <summary> /// 对象池管理器。可以通过这个管理器从对象池生成游戏对象,也可以回收游戏对象进对象池。 /// </summary> public class ObjectPoolsManager : SingletonPatternBase<ObjectPoolsManager> { //所有对象池的父物体 GameObject poolsParent; //所有对象池的共同父物体在Hierarchy窗口的名字 string poolsParentName = "ObjectPools"; //当前所有对象池的列表 public List<ObjectPool> objectPoolsList = new List<ObjectPool>(); //用来表示游戏对象所属的对象池 public Dictionary<GameObject, ObjectPool> objectsDictionary = new Dictionary<GameObject, ObjectPool>(); /// <summary> /// 从对象池生成游戏对象。 /// 如果对象池有,从对象池中取出来用。 /// 如果对象池没有,实例化该对象。 /// </summary> public GameObject Spawn(GameObject prefab,Vector3 position,Quaternion rotation,Transform parent=null) { if (prefab == null) return null; //如果场景中没有对象池的父物体,则生成一个空物体,作为所有对象池的父物体 CreatePoolsParentIfNull(); //先通过预制体来查找它所属的小对象池,如果找到了,则返回这个小对象池。 //如果找不到,则创建一个小对象池,用来存放这种预制体。 ObjectPool objectPool = FindPoolByPrefabOrCreatePool(prefab); GameObject go = objectPool.Spawn(position, rotation, parent); objectsDictionary.Add(go, objectPool); return go; } public void Despawn(GameObject go,float delayTime=0) { if (go == null) return; MonoManager.Instance.StartCoroutine(DespawnCoroutine(go,delayTime)); } IEnumerator DespawnCoroutine(GameObject go, float delayTime = 0) { //等待指定秒数 if (delayTime > 0) yield return new WaitForSeconds(delayTime); if (objectsDictionary.TryGetValue(go, out ObjectPool pool)) { objectsDictionary.Remove(go); pool.Despawn(go); } else { //获取这个游戏对象所属的对象池 pool = FindPoolByUsedGameObject(go); if (pool != null) { pool.Despawn(go); } } } ObjectPool FindPoolByUsedGameObject(GameObject go) { if (go == null) return null; for (int i = 0; i < objectPoolsList.Count; i++) { ObjectPool pool = objectPoolsList[i]; for (int j = 0; j < pool.usedGameObjectList.Count; j++) { if (pool.usedGameObjectList[j] == go) return pool; } } return null; } //如果场景中没有对象池的父物体,则生成一个空物体,作为所有对象池的父物体。 void CreatePoolsParentIfNull() { if (poolsParent==null) { objectPoolsList.Clear(); objectsDictionary.Clear(); poolsParent =new GameObject(poolsParentName); } } //先通过预制体来查找它所属的小对象池,如果找到了,则返回这个小对象池。 //如果找不到,则创建一个小对象池,用来存放这种预制体。 ObjectPool FindPoolByPrefabOrCreatePool(GameObject prefab) { //确保大对象池是存在的 CreatePoolsParentIfNull(); //查找并返回该预制体对数的对象池 ObjectPool objectPool= FindPoolByPrefab(prefab); if (objectPool==null) { objectPool = new GameObject($"ObjectPool{prefab.name}").AddComponent<ObjectPool>(); objectPool.prefab = prefab; objectPool.transform.SetParent(poolsParent.transform); objectPoolsList.Add(objectPool); } return objectPool; } ObjectPool FindPoolByPrefab(GameObject prefab) { if (prefab == null) return null; for (int i = 0; i < objectPoolsList.Count; i++) { if (objectPoolsList[i].prefab == prefab) return objectPoolsList[i]; } return null; } }

using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 对象池 /// </summary> public class ObjectPool : MonoBehaviour { //这个对象池存储的游戏对象的预制体 public GameObject prefab; //对象池最多能容纳多少个游戏对象。负数表示可以容纳无数个。 public int capacity = -1; //从这个对象池中取出并正在使用的游戏对象。 public List<GameObject> usedGameObjectList = new List<GameObject>(); //存在这个对象池中没有被使用的游戏对象。 public List<GameObject> unusedGameObjectList = new List<GameObject>(); //这个对象池中正在使用和没有被使用的游戏对象的总数。 public int TotalGameObjectCount { get => usedGameObjectList.Count + unusedGameObjectList.Count; } /// <summary> /// 从对象池获取一个对象,并返回这个对象。 /// 如果对象池有,从对象池中取出来用。 /// 如果对象池没有,则实例化该对象。 /// </summary> /// <param name="position"></param> /// <param name="rotation"></param> /// <param name="parent"></param> /// <returns></returns> public GameObject Spawn(Vector3 position,Quaternion rotation,Transform parent=null) { //要实例化的游戏对象 GameObject go; //如果对象池中有,则从对象池中取出来用。 if (unusedGameObjectList.Count>0) { go = unusedGameObjectList[0]; unusedGameObjectList.RemoveAt(0); usedGameObjectList.Add(go); go.transform.localPosition = position; go.transform.localRotation = rotation; go.transform.SetParent(parent, false); go.SetActive(true); } //如果对象池中没有,则实例化该对象。 else { go = Instantiate(prefab, position, rotation, parent); usedGameObjectList.Add(go); } //如果该游戏对象身上继承MonoBehaviour的脚本中写了名叫OnSpwan的方法,则会执行它们一次。 go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver); return go; } /// <summary> /// 隐藏指定的游戏对象,并把它回收进对象池中。 /// </summary> /// <param name="go"></param> public void Despawn(GameObject go) { if (go == null) return; //遍历这个对象池中所有正在使用的游戏对象 for (int i = 0; i < usedGameObjectList.Count; i++) { if (usedGameObjectList[i]==go) { //如果这个对象池的容量不为负数,且容纳的游戏对象已经满,则把0号的游戏对象删除掉,确保之后新的游戏对象能放入池子中。 if (capacity>=0&&usedGameObjectList.Count>=capacity) { if (unusedGameObjectList.Count>0) { Destroy(unusedGameObjectList[0]); unusedGameObjectList.RemoveAt(0); } } //把游戏对象回收到对象池中 unusedGameObjectList.Add(go); usedGameObjectList.RemoveAt(i); //如果该游戏对象身上继承MonoBehaviour的脚本写了名叫OnDespawn的方法,则在回收的时候,会执行一次。 go.SendMessage("OnDespawn",SendMessageOptions.DontRequireReceiver); go.SetActive(false); go.transform.SetParent(transform, false); return; } } } /// <summary> /// 把通过这个对象池生成的所有游戏对象全部隐藏并放入对象池中 /// </summary> public void DespawnAll() { int count = usedGameObjectList.Count; for (int i = 1; i <= count; i++) { Despawn(usedGameObjectList[0]); } usedGameObjectList.Clear(); } /// <summary> /// 预先往这个对象池中加载指定数量的游戏对象 /// </summary> public void Preload(int amount=1) { if (prefab == null) return; if (amount <= 0) return; for (int i = 1; i <= amount; i++) { GameObject go = Instantiate(prefab,Vector3.zero,Quaternion.identity); go.SetActive(false); go.transform.SetParent(transform, false); unusedGameObjectList.Add(go); go.name = prefab.name; } } }

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值