你都学会栈和队列了赶紧手搓一个对象池吧!!!(超详细,超简单适合新手宝宝学习)
前置知识:en造数据结构与算法C# 用数组实现个栈还不简单???看我一秒破之!!!(unity演示)-CSDN博客
c#有官方造好的关于stack的轮子,建议学习学习拿来直接用
本节实现目标:(注意右侧的Cube数量没有增加而是循环激活和失活)
1.对象池详解
对象池是一种优化游戏性能的技术,特别适用于需要频繁创建和销毁对象的场景,比如子弹、敌人、特效等。在Unity中,对象池可以显著减少内存分配和垃圾回收的开销,从而提高游戏的运行效率
流程图解:对象池的基本原理无非就三个步骤:创建指定数量的物体,调用物体(激活%拿出),释放物体(失活&放回),之后就是激活&拿出和失活&放回之间的循环了
内存图解:因为对象池就像是一个定长数组一样,所以内存是一连串而非碎片化的
对象状态图解:传统创造和销毁物体后,需要新的物体就要再创建
对象池是把需要销毁的物体重新放回池子里
2.代码详解
之所以全部拿过来了,是因为我注释写的巨详细,直接看下去会很连贯
using System.Collections.Generic;
using UnityEngine;public class MyPool {//stack作为对象池容器private Stack<GameObject> pool;//玩家可能需要调用的预制体private GameObject prefab;//像对象池创建预制体prefab和指定的数量initialCapacitypublic MyPool(GameObject prefab, int initialCapacity) {this.prefab = prefab;//实例化容器对象pool = new Stack<GameObject>(initialCapacity);for (int i = 0; i < initialCapacity; i++) {//存储实例化物体GameObject obj = GameObject.Instantiate(prefab);//失活obj.SetActive(false);//压栈pool.Push(obj);}}//玩家调用物体public GameObject Get() {//保险判断,如果对象池中有可用对象就弹栈返回出该对象if (pool.Count > 0) {return pool.Pop();}//如果没有的话,就创建并失活并返回else {GameObject obj = GameObject.Instantiate(prefab);obj.SetActive(false);//这里可以改成ture//至于为什么不压栈了,因为直接创建直接返回出去就得了,再压栈就还需要再弹栈,代码就重复了// pool.Push(obj); 如果有这行//return pool.Pop(); 那么得有这行return obj;}}//释放物体public void Release(GameObject obj) {//失活obj.SetActive(false);pool.Push(obj);}
}
3.使用示例
using System.Collections;
using UnityEngine;public class UsePool : MonoBehaviour {//我用来演示的方块预制体public GameObject cubePrefab;//对象池对象private MyPool myPool;void Start() {//向对象池放东西myPool = new MyPool(cubePrefab, 10);}void Update() {//通过旧输入系统,按下空格就使用对象池的调用物体方法if (Input.GetKeyDown(KeyCode.Space)) {GameObject obj = myPool.Get();obj.transform.position = Random.insideUnitSphere * 5; // 随机位置obj.SetActive(true);//如果你的get方法里面obj.SetActive(false);改成ture了,就不需要这行代码了StartCoroutine(AutoRelease(obj, 2f)); // 启动协程,2秒后自动释放}}private IEnumerator AutoRelease(GameObject obj, float delay) {//注意是先等两秒再释放,第一次调用直接释放会出现空引用或者其他有意思的情况,你可以复制下来自己试试yield return new WaitForSeconds(delay);myPool.Release(obj);}
}
4.一个小细节(重要)
在第二个大标题,也就是MyPool代码中的Get方法中,有一处可以改成ture
那么我为什么还要在UsePool类中添加这行
是因为,你传进来的是一个预制体,难免预制体上会挂在脚本,或者像我一样在UsePool类中的Update里面写一些逻辑
如果你在Get中激活了,那么我的逻辑赶不上你激活的速度,就会发生意想不到事情