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