Unity如何配置和使用一个对象池?
Closed this issue · 0 comments
Ferpakenameyea commented
考虑到这次游戏开发里有许多东方众,那么在开发中肯定少不了子弹的大量实例化和使用。
最简单的处理方法如下,每次想要使用的时候
var bullet = Instantiate(bulletPrefab);
也就是每次都根据预制件实例化一个新的子弹。
但!是!
频繁的新建和Destroy
实际上是对系统资源的一种浪费,如果是少量的物体,可能无伤大雅。不过如果是像弹幕类型的全屏大量实例化,会给GC系统带来不小的压力(甚至有可能卡飞!)
其实我们发现很多子弹在使用之后没必要直接Destroy
,可以收集起来之后继续使用!这种**就是对象池,幸运的是,Unity为我们内置了一个好用的对象池ObjectPool<>
,因此我们只需要简单地使用!
下面是一个简单的示例:
public class PoolingTest : MonoBehaviour
{
// 加上序列化字段的特性,让unity可以拖动赋值
[SerializeField] private GameObject bulletPrefab;
private ObjectPool<GameObject> objectPool;
private void Start()
{
this.objectPool = new ObjectPool<GameObject>(
() => Instantiate(bulletPrefab),
obj => obj.SetActive(true),
obj => {
obj.SetActive(false);
obj.transform.SetParent(this.transform);
},
obj => Destroy(obj),
defaultCapacity: 20,
maxSize: 10000
);
}
private void Action()
{
// 获取一个子弹实例
var bullet = objectPool.Get();
// 让子弹的MonoBehaviour开始运行 ^-^
// ...
// 当你不需要它的时候……
objectPool.Release(bullet);
}
private void OnDestroy()
{
objectPool.Dispose();
}
}
在这个示例中,我们新建了一个objectPool
,里面传了好几个lambda表达式,他们是干什么的呢?
() => Instantiate(bulletPrefab)
告诉了对象池应该怎么获取新的实例,如果不够的话就找这个函数要!
obj => obj.SetActive(true)
告诉了对象池当取出对象的时候,应当将其设置为active
obj => {
obj.SetActive(false);
obj.transform.SetParent(this.transform);
},
告诉了对象池当收回对象的时候,将其置为inactive
,并且把它变成自己的子对象。
为什么要变成自己的子对象呢?
因为用户可能在外部将对象放在了一个可能切换掉的场景里,如果场景被切换之后,这个游戏对象又被Release
回去,而在unity看来,被摧毁的游戏对象和null
没有区别(因此unity对象不可用?
来判断空),会引发异常!qwq
所以尽量把它作为自己的子对象,让对象和对象池共存亡
obj => Destroy(obj),
告诉了当摧毁对象池时应该做什么,我们可以直接销毁所有的实例
而后面两个参数,聪明如你应该能看出来啦!它表示对象池的初始容量和最大容量
现在,我们就可以使用对象池来获取游戏对象和回收游戏对象,避免了大量不必要的内存申请和GC操作~ ^w^