Unity 对象池技术
介绍
是什么? | 在开始时初始化若干对象,将它们存到对象池中。需要使用的时候从对象池中取出,使用完后重新放回对象池中。 |
优点 | 可以避免频繁创建和销毁对象带来性能消耗。 |
适用场景 | 如果需要对某种对象进行频繁创建和销毁时,例如应用在发射子弹、多个敌人创建等 |
代码逻辑
Unity中实现思路
1、设定初始化数量,创建List列表作为对象池容器。
2、初始化时通过Instantiate方法根据指定数量实例化一批对象,并存入List容器中。
3、使用时从容器中取出一个可用对象并SetActive(true),取出后从容器中移除。
4、使用完毕后,将对象SetActive(false),并重新放回List容器中。
主要方法
//初始化对象池
private void InitPool()
{
}
//创建单个对象池中的对象
private GameObject CreatePoolCell()
{
}
//从对象池中取出可用对象
private GameObject GetObjectFromPool()
{
}
//将对象放回对象池中
private void BackObjectToPool(GameObject obj)
{
}
//删除对象池
private void DestroyObjectPool()
{
}
示例代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public GameObject template;
public Transform parentRoot;
public Transform newRoot;
private List<GameObject> objectPool;
private int initCount;//对象池初始化的数量
void Start()
{
InitPool();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var obj = GetObjectFromPool();
ResetLocalPos(obj, newRoot);
StartCoroutine(Check1(obj));
}
}
IEnumerator Check1(GameObject obj)
{
yield return new WaitForSeconds(5f);
BackObjectToPool(obj);
}
#region 对象池逻辑
//初始化对象池
private void InitPool()
{
initCount = 10;
objectPool = new List<GameObject>();
for (var i = 0; i < initCount; i++)
{
var obj = CreatePoolCell();
objectPool.Add(obj);
}
}
//创建单个对象池中的对象
private GameObject CreatePoolCell()
{
var obj = Instantiate(template, parentRoot);
obj.SetActive(false);
obj.transform.localPosition = Vector3.zero;
return obj;
}
//从对象池中取出可用对象
private GameObject GetObjectFromPool()
{
for (var i = 0; i < objectPool.Count; i++)
{
var obj = objectPool[i];
if (!obj.activeInHierarchy)
{
obj.SetActive(true);
objectPool.Remove(obj);
return obj;
}
}
var newObj = CreatePoolCell();
newObj.SetActive(true);
return newObj;
}
//将对象放回对象池中
private void BackObjectToPool(GameObject obj)
{
if (objectPool.Contains(obj))
{
return;
}
obj.SetActive(false);
ResetLocalPos(obj, parentRoot);
objectPool.Add(obj);
}
//删除对象池
private void DestroyObjectPool()
{
foreach (var iconObj in objectPool)
{
DestroyImmediate(iconObj);
}
objectPool.Clear();
}
private void ResetLocalPos(GameObject obj,Transform parent)
{
var trans = obj.transform;
trans.SetParent(parent);
trans.localPosition = Vector3.zero;
}
#endregion
}
Unity自带对象池
构造函数
//将会在创建新对象的时侯调用
Func<T> createFunc,
//会在从池子获取对象的时侯调用
Action<T> actionOnGet = null,
//将对象放回池子里的时侯调用
Action<T> actionOnRelease = null,
//会在彻底销毁对象的时侯调用
Action<T> actionOnDestroy = null,
//安全检查,防止将已经回收过的对象进行再一次的重复回收,默认参数的true即可
bool collectionCheck = true,
//池子初始的默认大小,会在初始化时创建一个该容量大小的 stack
//需要根据自己项目的实际需求去权衡一下初始容量的大小。
int defaultCapacity = 10,
//主要是防止对象池内存过量增长,限定的对象池内最大可容纳的对象数量,
//如果池子超出了这个大小,接下来的回收对象将不会回到对象池
//而是直接调用它的 actionOnDestroy 回调进行销毁操作。
int maxSize = 10000)
主要属性和方法
接口 | 类型 | 描述 |
CountActive | 属性 | 正在使用(即被激活的)的对象数量 |
CountInactive | 属性 | 可以重用的对象数量 |
CountAll | 属性 | 正在使用的对象和可以重用的对象的总数量 |
Get | 方法 | 从对象池中获取对象 |
Release | 方法 | 将对象放回池子中 |
Clear | 方法 | 清理对象池 |
示例代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
public class Test : MonoBehaviour
{
public GameObject template;
public Transform parentRoot;
public Transform newRoot;
private List<GameObject> objectPool;
private int initCount;//对象池初始化的数量
private ObjectPool<GameObject> testPool;
public bool useObjectPool;
void Start()
{
testPool = new ObjectPool<GameObject>(() =>
{
//createFunc
//将会在创建新对象的时侯调用
var obj = Instantiate(template, parentRoot);
obj.SetActive(false);
obj.transform.localPosition = Vector3.zero;
return obj;
},
(go) =>
{
// actionOnGet
// 会在通过池子获取对象的时侯调用,
go.SetActive(true);
var trans = go.transform;
trans.SetParent(newRoot);
trans.localPosition = Random.insideUnitSphere;
},
(go) =>
{
// actionOnRelease
// 在对象放回池子里的时侯调用,这里我们取消激活需要放回池中的对象
go.SetActive(false);
var trans = go.transform;
trans.SetParent(parentRoot);
trans.localPosition = Vector3.zero;
},
(go) =>
{
/* actionOnDestroy 会在彻底销毁对象的时侯调用
这里直接去destroy它就可以了
对象池会在你手动释放对象或者内部空间无法存储你返回的对象的时侯
调用这个函数来销毁它们
*/
Destroy(go);
});
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var obj = testPool.Get();
StartCoroutine(Check1(obj));
}
}
IEnumerator Check1(GameObject obj)
{
yield return new WaitForSeconds(5f);
testPool.Release(obj);
}
}