美文网首页
Unity高级 -- 对象池(Object Pool)

Unity高级 -- 对象池(Object Pool)

作者: Shawn_正品鞋铺 | 来源:发表于2017-03-30 18:44 被阅读529次

我们在做项目的时候,如果同一个物体要用到好多次,我们就不要再用实例化了,因为每次创建一个新物体是很消耗资源的,所以我们需要引用对象池技术

什么是对象池?

对象池就是我们将对象存储在一个池子中,当需要时在再次使用,而不是每次都实例化一个新的对象。池的最重要的特性,也就是对象池设计模式的本质是允许我们获取一个“新的”对象而不管它真的是一个新的对象还是循环使用的对象。在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上)

五.对象池效果

相关文章

网友评论

      本文标题:Unity高级 -- 对象池(Object Pool)

      本文链接:https://www.haomeiwen.com/subject/hsypottx.html