Unity游戏框架(一)对象池

出于对性能的考量,对象池是所有项目都无法绕开的点。

从项目的实际需求和DEMO时的表现来看,我首先需要搭建对象池用于优化游戏性能,毕竟iPhoneX都卡的游戏能卖给谁呢。

单例

创建我们的对象池,首先从单例开始,单例有许多种写法,根据需求来就好了,不必搞得太复杂。

我现在的第一个需求是存放场景中不断重复生成的物体,比如子弹,敌人。

    /// <summary>
    /// 对象池词典
    /// </summary>
    private Dictionary<string, List<GameObject>> pool;
    /// <summary>
    /// 预制体词典
    /// </summary>
    private Dictionary<string, GameObject> prefabs;
    
    private GameObjectPool()
    {
        pool = new Dictionary<string, List<GameObject>>();
        prefabs = new Dictionary<string, GameObject>();
    }
    
    private static GameObjectPool instance;
    
    public static GameObjectPool GetInstance()//外部入口
    {
        if (instance == null)
        {
            instance = new GameObjectPool();
        }
        return instance;
    }

unity2018以前的版本无法使用ECS,所以默认只有主线程才可以调用unity的API,所以我们默认线程安全。

获取对象池中的对象

当我们需要使用某个对象比如子弹时,从池中检查是否存在,如果不存在就Resources.Load()。

    /// <summary>
    /// 从对象池中获取对象
    /// </summary>
    /// <param name="objName">试图获取的对象名</param>
    /// <returns>返回对象</returns>
    public GameObject GetObj(string objName)
    {
        GameObject result = null;//最终返回的结果
        GameObject prefab = null;//预制体加载

        //情况1: 池中已存在objName且池中存在待使用的GameObject
        if (pool.ContainsKey(objName))
        {
            if (pool[objName].Count > 0)//如果池中存在GameObject
            {
                result = pool[objName][0];//取出GameObject
                pool[objName].Remove(result);//删除池中的记录
                result.SetActive(true);//激活GameObject
                return result;//返回结果
            }
        }

        //情况2: 池中不存在objName或池中不存在待使用的GameObject
        if (prefabs.ContainsKey(objName))//已加载过对应的预制体
        {
            prefab = prefabs[objName];
        }
        else                             //未加载过该预设体
        {
            //加载预设体
            prefab = Resources.Load<GameObject>("Prefabs/" + objName);
            //更新字典
            prefabs.Add(objName, prefab);
        }
        
        result = Object.Instantiate(prefab);//生成新的预制体克隆
        result.name = objName;//改名(去除 Clone)
        return result;//返回
    }

回收游戏对象

当某个对象不再被需要时,我们可以将其回收进对象池保存起来,准备下一次的使用。

    /// <summary>
    /// 回收对象到对象池
    /// </summary>
    /// <param name="objName"></param>
    public void RecycleObj(GameObject obj)
    {
        //设置为非激活
        obj.SetActive(false);
        //判断是否有该对象的对象池
        if (pool.ContainsKey(obj.name))
        {
            //放置到该对象池
            pool[obj.name].Add(obj);
        }
        else
        {
            //创建该类型的池子,并将对象放入
            pool.Add(obj.name, new List<GameObject>() { obj });
        }
    }

还需要一段附着在预制体上的代码,用以自动激活回收这一行为。

    void Start()
    {
        StartCoroutine(AutoRecycle());
    }
    /// <summary>
    /// 3秒后自动回收到对象池
    /// </summary>
    IEnumerator AutoRecycle()
    {
        yield return new WaitForSeconds(3f);

        GameObjectPool.GetInstance().RecycleObj(gameObject);
    }

当然你可以在任何你想要的时候去回收它。

猜你喜欢

转载自blog.csdn.net/Testiness_Wind/article/details/81118772