欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity对象池应用

程序员文章站 2022-07-13 15:00:51
...

对象池,顾名思义,就是放对象的池子。
优缺点:空间换时间,用额外的内存消耗减少创建物体的时间,保证运行的流畅度。
何时用:当需要大量创建相同游戏物体时,例如射击游戏中不停的创建子弹销毁子弹。大量地创建物体销毁物体会造成游戏卡顿,为了解决这个方法可以采用对象池。

实现
1、需要一个容器,用来实现存放对象
2、一个取对象的方法
3、一个存对象的方法
详细代码如下:
关键地方已写了详细备注。

public class GameObjectPool : MonoBehaviour {

    private static GameObjectPool instance;
    public static GameObjectPool Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject obj = new GameObject("GameObjectPool");
                instance = obj.AddComponent<GameObjectPool>();
                DontDestroyOnLoad(obj);
            }
            return instance;
        }
    }
    //对象池
    public Dictionary<string, List<GameObject>> cache;

    public GameObject GetGameObject(string key, GameObject prefab, Vector3 position)
    {
        GameObject obj;
        //如果字典中存在当前键,并且当前键的对象池里有未使用的游戏物体,则直接拿出一个使用。
        if (cache.ContainsKey(key) && cache[key].Where(w => w.activeSelf == false).Count() > 0)
        {
            obj = cache[key][0];
            cache[key].RemoveAt(0);
        }
        //如果字典中存在当前键,但是当前键的对象池物体已经被用完,则根据传进来的预制体新创建一个。
        else if (cache.ContainsKey(key) && cache[key].Where(w => w.activeSelf == false).Count() <= 0)
        {
            obj = Instantiate(prefab) as GameObject;
        }
        //字典中不存在当前键,新建一个键与相对应的对象池,并根据传进来的预制体新创建一个
        else
        {
            obj = Instantiate(prefab) as GameObject;
            cache.Add(key, new List<GameObject>());
        }
        obj.SetActive(true);
        obj.transform.position = position;
        obj.transform.SetParent(transform);
        return obj;
    }
    //延迟回收
    public void CollectObject(string key,GameObject obj)
    {
        StartCoroutine(DelayCollect(key, obj));
    }
    //回收对象池的方法,将物体放回对象池并且设置为false
    IEnumerator DelayCollect(string key,GameObject obj)
    {
        cache[key].Add(obj);
        yield return new WaitForSeconds(2);
        obj.SetActive(false);
    }
    //清空对象池中没有使用的对象
    public void Clear(string key)
    {
        if (cache.ContainsKey(key))
        {
            //Destroy当中所有的对象
            for (int i = 0; i < cache[key].Where(w => w.activeSelf == false).Count(); i++)
            {
                Destroy(cache[key][i]);
            }
            if (cache[key].Where(w => w.activeSelf == true).Count() <= 0)
                cache.Remove(key);
        }
    }
    public void AllClear()
    {
        var list = new List<string>(cache.Keys);
        for (int i = 0; i < list.Count; i++)
        {
            Clear(list[i]);
        }
    }
    private void Awake()
    {
        cache = new Dictionary<string, List<GameObject>>();
    }
}

对象池测试脚本代码如下:

public class PoolTest : MonoBehaviour {

    GameObject prefabCube;

    GameObject prefabSphere;

    GameObject go;
    private void Start()
    {
        Init();
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            go = GameObjectPool.Instance.GetGameObject("Cube", prefabCube, new Vector3(Random.Range(-4, 4), Random.Range(-4, 4), 0));
            go.name = "Cube";
            GameObjectPool.Instance.CollectObject(go.name, go);
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            go = GameObjectPool.Instance.GetGameObject("Sphere", prefabSphere, new Vector3(Random.Range(-4, 4), Random.Range(-4, 4), 0));
            go.name = "Sphere";
            GameObjectPool.Instance.CollectObject(go.name, go);
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            GameObjectPool.Instance.AllClear();
        }

    }
    private void Init()
    {
        prefabCube = Resources.Load("Prefab/Cube") as GameObject;
        prefabSphere = Resources.Load("Prefab/Sphere") as GameObject;
    }
}

实际演示效果:
Unity对象池应用
可以看到,当对象池有物体时并且可用数量不为0时,会直接将对象池的物体拿来用,不存在或者可用数量为0时才会新建。
ps:实现对象池的方式多种多样,什么时候回收也要根据具体的条件做适应的修改,这里只是做一个最简单的案例。
案例Demo