Unity3d对象池

目的:以后项目需要对象池,直接将这四个脚本放入工程调用PoolManager.SpawnObject()创建物体,调用PoolManager.ReleaseObject(this.gameObject);释放物体即可实现,效果:

对象池实现,三个对象池相关类 + 一个单例类(这个单例类挺好,所以加上了)

直接将这四个脚本放入工程:

1、在场景中只挂在PoolMnanager脚本,指定一个存放物体的对象(Root);

2、调用PoolManager.SpawnObject(prefab,position,Quaternion.identity),第一个参数是想要生成的预制体对象,第二个参数是位置,第三个是四元素的角度。

示例源代码:点击一次鼠左键标生成一个Cube,Cube上挂载一个脚本,自动向前运动,2秒后释放此对象;

1、三个对象池相关类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectPoolContainer<T>
{
    private T item;//对象(最小单元)

    public T Item { get => item; set => item = value; }

    public bool Used { get; private set; }

    public void Consume()//设置为使用状态
    {
        Used = true;
    }

    public void Release()//设置为未使用状态
    {
        Used = false;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;

public class ObjectPool<T>
{
    public List<ObjectPoolContainer<T>> list;//未使用的对象列表

    public Dictionary<T, ObjectPoolContainer<T>> lookup;//正在使用的对象字典

    private Func<T> factoryFunc;

    private int lastIndex = 0;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="factoryFunc">返回一个T类型的对象</param>
    /// <param name="initialSize">初始化大小</param>

    public ObjectPool(Func<T> factoryFunc, int initialSize)
    {
        this.factoryFunc = factoryFunc;

        list = new List<ObjectPoolContainer<T>>(initialSize);

        lookup = new Dictionary<T, ObjectPoolContainer<T>>(initialSize);

        Warm(initialSize);
    }

    /// <summary>
    /// 创建对象池
    /// </summary>
    /// <param name="capacity">大小</param>
    private void Warm(int capacity)
    {
        for (int i = 0; i < capacity; i++)
        {
            CreateContainer();
        }
    }

    /// <summary>
    /// 创建对象
    /// </summary>
    /// <returns></returns>
    private ObjectPoolContainer<T> CreateContainer()
    {
        var container = new ObjectPoolContainer<T>();
        container.Item = factoryFunc();
        list.Add(container);
        return container;
    }

    /// <summary>
    /// 从池中获取一个对象
    /// </summary>
    /// <returns></returns>
    public T GetItem()
    {
        ObjectPoolContainer<T> container = null;

        for (int i = 0; i < list.Count; i++)//从位置用的list中找未使用的对象
        {
            lastIndex++;
            if (lastIndex > list.Count - 1)
            {
                lastIndex = 0;
            }

            if (list[lastIndex].Used)//正在使用
            {
                continue;
            }
            else//未使用
            {
                container = list[lastIndex];
            }
        }
        if (container == null)//没有的话就创建一个新的
        {
            container = CreateContainer();
        }
        container.Consume();//标记为使用状态
        lookup.Add(container.Item, container);//添加到正在使用的字典中
        return container.Item;
    }

    /// <summary>
    /// 释放不用的游戏对象
    /// </summary>
    /// <param name="item"></param>
    public void ReleaseItem(object item)
    {
        ReleaseItem(item);
    }

    public void ReleaseItem(T item)
    {
        if (lookup.ContainsKey(item))//存在
        {
            var container = lookup[item];
            container.Release();释放此物体
            lookup.Remove(item);从正在使用的字典中移除
        }
        else
        {
            Debug.LogWarning("this object pool does not contain the item provided:" + item);
        }
    }

    public int Count
    {
        get { return list.Count; }
    }

    public int CountUsedItems
    {
        get { return lookup.Count; }
    }




    #region Func详解用法
    /* Func详解用法
    Func<T in, T Tresult>:封装一个具有一个参数并返回 TResult 参数指定的类型值的方法。其实个人感觉,Func和Action的区别很明显,也很直接。二者都是委托,但Func能返回函数执行结果,而Action返回类型是Void,这个区别很明显,在具体的项目中,也很容易确定该使用那个。下文就说明具体Func的代码调用:
    public string myName;
    Func<string, string> myFunc;
    public MyBlogBase()
    {
        //myFunc = delegate(string curName) { return curName.ToUpper(); };
        //myFunc = new Func<string, string>(SetFunc);
        myFunc = name => { return name.ToUpper(); };
    }
    private string SetFunc(string name)
    {
        return name.ToUpper();
    }
    public void StartFun(string curName)
    {
        myName = myFunc(curName);
    }
    如上3种写法,都是合适的Func定义,大家可以选择适合自己的编程模式,其实匿名方法,有个优点,就是可以直接使用当前函数出现的变量,代码更简洁,但可能有些人觉得不易读。
    */
    #endregion
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolManager : Singleton<PoolManager>
{
    public bool logStatus;
    public Transform root;

    private Dictionary<GameObject, ObjectPool<GameObject>> prefabLookup;//已经创建的对象池
    private Dictionary<GameObject, ObjectPool<GameObject>> instanceLookup;//当前使用的对象池

    //private bool dirty = false;//

    private void Awake()
    {
        prefabLookup = new Dictionary<GameObject, ObjectPool<GameObject>>();
        instanceLookup = new Dictionary<GameObject, ObjectPool<GameObject>>();
    }

    //private void Update()
    //{
    //    if (logStatus && dirty)
    //    {
    //        PrintStatus();
    //        dirty = false;
    //    }
    //}

    /// <summary>
    /// 创建一个对象池
    /// </summary>
    /// <param name="prefab">什么类型</param>
    /// <param name="size">大小</param>
    public void warmPool(GameObject prefab, int size)
    {
        if (prefabLookup.ContainsKey(prefab))
        {
            throw new System.Exception("pool for prefab " + prefab.name + "has already been created");
        }
        var pool = new ObjectPool<GameObject>(() => { return InstatiatePrefab(prefab); }, size);
        prefabLookup[prefab] = pool;
        //dirty = true;
    }

    /// <summary>
    /// 创建游戏对象,默认位置角度
    /// </summary>
    /// <param name="prefab"></param>
    /// <returns></returns>
    public GameObject spawnObject(GameObject prefab)
    {
        return spawnObject(prefab, Vector3.zero, Quaternion.identity);
    }

    /// <summary>
    /// 创建游戏对象,自定义位置,角度
    /// </summary>
    /// <param name="prefab">要创建的对象</param>
    /// <param name="position">坐标</param>
    /// <param name="rotation">角度</param>
    /// <returns></returns>
    public GameObject spawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
    {
        if (!prefabLookup.ContainsKey(prefab))
        {
            warmPool(prefab, 1);
        }
        var pool = prefabLookup[prefab];

        var clone = pool.GetItem();
        clone.transform.position = position;
        clone.transform.rotation = rotation;
        clone.SetActive(true);

        instanceLookup.Add(clone, pool);
        //dirty = true;
        return clone;
    }

    /// <summary>
    /// 释放目标对象
    /// </summary>
    /// <param name="clone"></param>
    public void releaseObject(GameObject clone)
    {
        clone.SetActive(false);

        if (instanceLookup.ContainsKey(clone))
        {
            instanceLookup[clone].ReleaseItem(clone);
            instanceLookup.Remove(clone);
            //dirty = true;
        }
        else
        {
            Debug.LogWarning("No pool contains the objcet : " + clone.name);
        }
    }

    /// <summary>
    /// 创建指定游戏对象(返回Func)
    /// </summary>
    /// <param name="prefab"></param>
    /// <returns></returns>
    private GameObject InstatiatePrefab(GameObject prefab)
    {
        var go = Instantiate(prefab) as GameObject;
        if (root != null) go.transform.parent = root;
        return go;
    }

    //public void PrintStatus()
    //{
    //    foreach (KeyValuePair<GameObject, ObjectPool<GameObject>> keyVal in prefabLookup)
    //    {
    //        Debug.Log(string.Format("Object pool for Prefab: {0} InUse:{1} Total {2}", keyVal.Key.name, keyVal.Value.CountUsedItems,keyVal.Value.Count));
    //    }
    //}


    //以下是做成了静态方法,直接通过类调用,可有可无,不写静态就直接通过单例来调用
    public static void WarmPool(GameObject prefab, int size)
    {
        Instance.warmPool(prefab, size);
    }

    public static GameObject SpawnObject(GameObject prefab)
    {
        return Instance.spawnObject(prefab);
    }

    public static GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
    {
        return Instance.spawnObject(prefab, position, rotation);
    }

    public static void ReleaseObject(GameObject clone)
    {
        Instance.releaseObject(clone);
    }
}

2、单例类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
    private static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];//尝试获取场景中当前所有的此对象
                if (managers.Length != 0)
                {
                    if (managers.Length == 1)//如果只有一个,那么直接return,找到合适的了,下边不执行了
                    {
                        instance = managers[0];
                        instance.gameObject.name = typeof(T).Name;
                        return instance;
                    }
                    else//若存在多个,则全部删除,之后重新建立符合条件的单例
                    {
                        Debug.LogError("Class" + typeof(T).Name + "exists multiple times in volation of singleton pattern. destroying all copies");
                        foreach (var item in managers)
                        {
                            Destroy(item.gameObject);
                        }
                    }
                }
                var go = new GameObject(typeof(T).Name, typeof(T));//创建此对象的单例
                instance = go.GetComponent<T>();
                DontDestroyOnLoad(go);
            }
            return instance;
        }
        set { instance = value as T; }
    }
}

3、Cube移动、自释放的控制

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CubeStatus : MonoBehaviour
{
    private float timeCube = 2;//移动时间

    private void Update()
    {
        this.transform.Translate(Vector3.forward * Time.deltaTime * 30, Space.World);//移动
        if (timeCube >0)
        {
            timeCube -= Time.deltaTime;
            if (timeCube <=0)
            {
                timeCube = 2;
                PoolManager.ReleaseObject(this.gameObject);
            }
        }
    }
}

4、Cube的创建

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CubeController : MonoBehaviour
{

    public GameObject myCubePrefab;//预制体,手动赋值
    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            print("按下了鼠标右键");
            Vector3 v = new Vector3(Input.mousePosition.x, Input.mousePosition.y,10f);
            PoolManager.SpawnObject(myCubePrefab, Camera.main.ScreenToWorldPoint(v), Quaternion.identity);
        }
    }
}

注意:随便 创建一个Cube做成预制体,将此预制体托给脚本CubeController ,记得给Root赋值(空物体就行)

完成!

猜你喜欢

转载自blog.csdn.net/SandmanRUN/article/details/108210200