频繁创建和销毁对象会造成性能的开销。
创建对象的时候,系统会为这个对象开辟一片新的空间。销毁对象的时候,这个对象会变成内存垃圾,当内存垃圾达到一定程度,就会触发垃圾回收机制,清理内存垃圾,由于此时在清理垃圾,所以程序有可能就会变卡。
为了改善这个问题,我们就可以使用对象池。使用了它之后,程序的性能就能得到提升不那么容易变卡。
1、当要创建对象的时候,不直接创建,而是先从对象池里面找,如果对象池里有这种类型的对象,就从对象池中取出来用。如果对象池里面没有这种类型的对象,才创建该对象。
2、当要销毁对象的时候,不直接销毁,而是把这个对象放进对象池里面存着,以便下一次创建对象时使用。
一个预制体对应一个对象池,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 对象池
///
public class ObjectPool : MonoBehaviour
{
public GameObject prefabs; //这个对象池存储的游戏对象的预制体
public int capcity = 100; //对象池的容量
//从这个对象池中取出来正在使用的游戏对象
public List usedGameObjectList = new List();
//存在这个对象池中没有被使用的游戏对象
public List unusedGameObjectList = new List();
//这个池子总共拥有的游戏对象的个数
public int TotalGameObjectCount { get => usedGameObjectList.Count + unusedGameObjectList.Count; }
///
/// 从对象池中获取一个对象,并返回一个对象
///
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(prefabs, position, rotation, parent);
usedGameObjectList.Add(go);
}
//如果该游戏对象身上继承MonoBehaviour的脚本中写了名叫OnSpwan的方法,则会执行它们一次
// 就算没有接收者也不报错
go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);
return go;
}
//回收进对象池中
public void DesPawn(GameObject go)
{
if (go == null) return;
//遍历这个对象池中所有正在使用的游戏对象
for (int i = 0; i < usedGameObjectList.Count; i++)
{
if (usedGameObjectList[i] == go)
{
//如果这个对象池的容量不为负数,且容纳的游戏对象已经满了,则把0号游戏对象删除掉
//确保之后新的游戏对象能放入池子中
if (capcity >= 0 && usedGameObjectList.Count >= capcity)
{
if (unusedGameObjectList.Count>0)
{
DesPawn(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;
}
}
}
///
/// 把通过这个对象池生成的所有对象全部隐藏并放入对象池中
///
public void DesPawnAll()
{
int count = usedGameObjectList.Count;
for (int i = 1; i <= count; i++)
{
DesPawn(usedGameObjectList[0]);
}
usedGameObjectList.Clear();
}
///
/// 预加载的方法,预先在池子里放几个对象
///
public void PreLoad(int amount = 1)
{
if (prefabs == null) return;
if (amount <= 0) return;
for (int i = 1; i <= amount ; i++)
{
GameObject go = Instantiate(prefabs, Vector3.zero, Quaternion.identity);
go.SetActive(false);
go.transform.SetParent(transform, false);
unusedGameObjectList.Add(go);
go.name = prefabs.name; //更改一下生成的物体的名字
}
}
}
一个预制体一个池子,然后一个大的池子管理所有的小对象池
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 对象池管理,可以通过这个管理器从对象池生成游戏对象,也可以回收游戏对象进对象池
///
public class ObjectPoolsManager : SingletonPatternBase
{
GameObject poolsParent; //所有对象池的父物体
string poolsParentName = "ObjectPools"; //大对象池的名字
//当前所有小对象池的列表
public List objectPoolsList = new List();
public Dictionary objectDictionary = new Dictionary();
///
/// 从对象池生成游戏对象
/// 如果对象池有,从对象池中取出来用
/// 如果对象池没有,实例化该对象
///
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);
objectDictionary.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)
{
//等待指定的秒数
yield return new WaitForSeconds(delayTime);
if (objectDictionary.TryGetValue(go, out ObjectPool pool))
{
objectDictionary.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();
objectDictionary.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.prefabs = 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].prefabs == prefab)
{
return objectPoolsList[i];
}
}
return null;
}
}
物体身上最好写一个这样的方法将物体状态初始化,清楚一些脏数据
void OnDespawn()
{
transform.position = Vector3.zero;
GetComponent().velocity = Vector3.zero;
}