美文网首页
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