我们在做项目的时候,如果同一个物体要用到好多次,我们就不要再用实例化了,因为每次创建一个新物体是很消耗资源的,所以我们需要引用对象池技术
什么是对象池?
对象池就是我们将对象存储在一个池子中,当需要时在再次使用,而不是每次都实例化一个新的对象。池的最重要的特性,也就是对象池设计模式的本质是允许我们获取一个“新的”对象而不管它真的是一个新的对象还是循环使用的对象。在FPS中常用作需要被发射的子弹、粒子等。
优点在于:优化内存资源,提高系统性能
接下来做一个简易的对象池,用来模拟手枪发射子弹
一.创建Cube(代替手枪模型),创建一个Sphere(子弹)用于预制存储
(方便分辨这里使用了两个shi一样颜色的Material....)
(将Sphere的Scale改小一点,Rigidbody的作用是让子弹是否有重力效果,所以是否添加取决你当前的项目是否需要)
二.创建对象池的脚本
public class GameObjectPool : MonoBehaviour
{
const int number = 10;//子弹弹夹的容量
bool isin = true;//用来判断是否实例子弹对象
int n;//用来记录实例出子弹对象的个数
//集合里面的元素,相当于对象池里面的对象,这里的集合可看作为对象池。
static List<GameObject> pools = new List<GameObject>();
//再声明一个集合,用来回收发射出去的子弹对象
List<GameObject> pb = new List<GameObject>();
//首先创建一个单例
private GameObjectPool() { }
private static GameObjectPool instance;
public static GameObjectPool GetInstance(GameObject name)//此处的Gameobject name就是需要放入对象池的对象--子弹
{
if (instance == null)
{
print("实例池子");
//动态的生成一个名为“GameObjectPool”的对象并将单例脚本附加上去
instance = new GameObject("GameObjectPool").AddComponent<GameObjectPool>();
for (int i = 0; i < number; i++)//先往对象池中放入十发子弹
{
name.SetActive(false);
pools.Add(name);
}
}
return instance;
}
//从对象池中取对象
public GameObject MyInstantiate()
{
if (pools.Count == 0)//如果十发子弹打完
{
print("没子弹了:" + pools.Count);
return null;
}
else
{
// print(pools[0].name);
//取出对象池里面的第一个元素
GameObject obj= pools[0];
if (isin)//判断是否已经实例了十发子弹
{
//obj = pools[0];
Instantiate(obj);//实例出子弹
n++;//计数加加
if (n == number)
{
isin = false;
}
}
//将对象设置为激活状态
obj.SetActive(true);
//将被取出的元素,从对象池中移除
pools.Remove(obj);
return obj;
}
}
//回收子弹的方法
public void MyDisable(GameObject name)
{
//将传进来的对象隐藏(处于非激活状态)
name.SetActive(false);
//回收子弹添加到pb集合中
pb.Add(name);
}
public void ResetBullet()//换弹夹的方法
{
if (pb.Count != 0)//判断回收子弹的集合是否是空
{
print("pb不是空");
for (int i = 0; i < pb.Count; i++)
{
pools.Add(pb[i]); //把回收的子弹重新加到弹夹中去
}
pb.Clear();
}
}
}
(脚本无需挂载)
三.创建预制子弹的脚本
public class Bullet : MonoBehaviour {
//public Transform target;
void OnEnable()
{
//脚本可用的时候,重置子弹的位置。
//如果不加这句代码,从对象池中取出的子弹就会从上一次消失的位置开始运动。而不是你设定的子弹生成位置
//transform.position = Vector3.zero;
//开启协程方法
StartCoroutine(DelayDisable(3f));
}
void Update()
{
//子弹生成,自动向前运动
transform.Translate(Vector3.forward * Time.deltaTime * 20);
}
void OnDisable()
{
Debug.Log("I'm over");
}
//子弹消失的方法
IEnumerator DelayDisable(float time)
{
//等待三秒
yield return new WaitForSeconds(time);
//调用单例中向对象池里面存对象的方法
GameObjectPool.GetInstance().MyDisable(gameObject);
}
}
(脚本挂载于Shpere预制上)
四.创建Cube的脚本,用于发射子弹
public class GameManerge : MonoBehaviour {
//创建子弹的预设体
public GameObject mBulletPrefab;
void Update()
{
//如果按下鼠标左键,发射子弹
if (Input.GetMouseButtonDown(0))
{
//调用单例脚本里面的从对象池中取对象的方法
GameObject go = GameObjectPool.GetInstance(mBulletPrefab).MyInstantiate();
if(go!=null)
{
go.transform.position = transform.position; //设置子弹生成的位置是Cube的位置
}
}
//按下鼠标右键,换弹夹
if (Input.GetMouseButtonDown(1))
{
print("换子弹:");
GameObjectPool.GetInstance(mBulletPrefab).ResetBullet();
}
}
}
(挂载于Cube上)
网友评论