对Unity有一点了解的人一定知道,实例化对象是非常消耗性能的,而摧毁对象消耗少一点但同样会影响性能,所以为了优化,把常用的对象存入对象池,在调用时从中取出,在不使用的时候隐藏放入对象池,这样就可以大大节省资源的消耗。
一.使用定制资源配置文件
在写对象池前需要知道的一些东西,这种定制资源配置技术可以让我们写的对象池更方便的使用,它可以让我们直接在一个保存的自定义配置文件中来进行对对象池的操作,运行的时候会自动帮我们加载,非常方便,当然我们也可以直接把写好的对象池脚本挂在一个对象把它做成Prefab来使用但很明显这样非常的不舒服且不方便。
并不需要非常清楚的了解这个技术,大概知道怎么使用就可以开始了。
二.创建GameObjectPool类
首先我们创建一个Pool类,它应该有名字,预物体,最大容量以及用以存储实例化对象的List集合,还要对外提供一个取对象的方法。
[Serializable]
public class GameObjectPool {
public string name;
[SerializeField]
private GameObject prefab;
[SerializeField]
private int maxCount;
[NonSerialized]
private List<GameObject> goList;
public GameObject GetInst()
{
foreach (GameObject item in goList) //遍历是否存在空闲的对象,如果有返回
{
if (!item.activeInHierarchy)
return item;
}
if(goList.Count>=maxCount) //发现不存在空闲对象,在创建新对象前要先判断是否达到容量,如果达到,销毁最早的对象
{
GameObject.Destroy(goList[0]);
goList.RemoveAt(0);
}
GameObject temp = GameObject.Instantiate(prefab) as GameObject; //创建新对象加入集合并返回
goList.Add(temp);
return temp;
}}
注意一定要在类前加上Serializable特性使Pool类可以被序列化,如果不想把有些字段写成public,可以在用SerializeField来修饰就可以在编辑器中操作了,对于不需要序列化的字段比如goList保存着游戏中生成的对象,不需要在配置文件中进行操作就可以加上NonSerialized特性即可
三.创建PoolList类
创建PoolList类,保存了对象池集合,关键在于它要继承ScriptableObject类
public class PoolList :ScriptableObject {
public List<GameObjectPool> poolList;
}
四.扩展Unity编辑器
[MenuItem("Manager/Create PoolListConfigure")]
static void CreatePoolListConfigure()
{
PoolList poolList=ScriptableObject.CreateInstance<PoolList>();
string path="Assets/Scripts/Pool/poolList.asset";
AssetDatabase.CreateAsset(poolList, path);
AssetDatabase.SaveAssets();
}
poolList的创建要使用ScriptableObject中的方法,最后我们使用AssetDatabase中的方法将对象序列化保存为asset文件,路径必须项目路径开始否则会报错。
五.创建PoolManager管理对象池
public class PoolManager {
private PoolManager _instance;
public PoolManager Instance
{
get
{
if (_instance == null)
_instance = new PoolManager();
return _instance;
}
}
private const string poolConfigurePath = "poolList";
private Dictionary<string, GameObjectPool> poolDic;
private PoolManager()
{
PoolList poolList = Resources.Load<PoolList>(poolConfigurePath);
poolDic=new Dictionary<string,GameObjectPool>();
foreach (GameObjectPool item in poolList.poolList)
{
poolDic.Add(item.name, item);
}
}
public GameObject GetInst(string poolName)
{
GameObjectPool pool;
if (poolDic.TryGetValue(poolName, out pool))
return pool.GetInst();
Debug.LogWarning("pool:" + poolName + "is not exist!");
return null;
}
public void Init()
{
}
}
代码很简单,作为管理类一般都要做成单例模式,在构造函数中加载之前生成的配置表,使用字典结构来存储每个对象池方便查找,对外提供取对象的方法,最后的Init()方法用来初始化,因为我们的构造函数是私有的,所以在外界调用Instance的时候才会构造,如果在调用GetInst方法时候去创建会导致明显的卡顿,所以只要在游戏开始前调用一下Init这个空方法就可以了。
实际上对象池我们还可以继续进行扩展,比如加上生命周期,让一些我们很久没有使用的对象在经过一段时间后自动销毁掉来节省内存,等等在需要的时候可以继续往深研究。