前置知识: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和指定的数量initialCapacity
public 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中激活了,那么我的逻辑赶不上你激活的速度,就会发生意想不到事情