天天看点

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

目录

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

五、实现步骤

六、关键代码

七、参考工程

八、参考资料

一、简单介绍

Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

简单的对象池管理类,对象池管理是一种广泛通用的内存优化手段。在首次建立对象时,便将其存储在池子中,需要再次使用时,不是每次都实例化一个新的对象,而是查找回收池中是否存在闲置的对象,将其重新激活利用。连续的生成和销毁游戏物体也是耗性能的。

二、实现原理

1、单例类,保证整个场景中只有一个类管理对象池;

2、把需要的对象以对象池形式管理起来,需要的时候 拿出来显示即可,不需要的时候隐藏以来即可;

3、ObjectPoolManager.Instance.WarmPool 孵化一些预制的对象池对象;

4、ObjectPoolManager.Instance.SpawnObject 取出未使用的对象池中的对象;

5、ObjectPoolManager.Instance.ReleaseObject 释放使用完的对象到对象池中;

三、注意事项

1、当对象池中的孵化的对象,不够用,会重新孵化新的;

2、注意游戏对象池的对象,在游戏结束切换场景后,有必要的话可以全部释放并销毁掉,视情况而定;

四、效果预览

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

五、实现步骤

1、打开Unity,新建一个工程,如下图

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

2、新建脚本,单例用于保证场景中只一个管理类,池对象的属性类管理对象的使用情况,对象池管理对象群的使用情况,对象池管理类管理所有对象池使用情况,如下图

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

3、新建测试对象池管理的脚本,和一个Move 移动和释放池对象的脚本,以及两个Cube 和 Sphere 预制体,并绑定 Move 脚本,如下图

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

4、运行场景,效果如下图

Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现Unity 工具类 之 简单的对象池管理类 ObjectPoolManager 实现

六、关键代码

1、TestScript

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

public class TestScript : MonoBehaviour
{
    public GameObject cube;
    public GameObject sphere;

    // Start is called before the first frame update
    void Start()
    {
        ObjectPoolManager.Instance.WarmPool(cube,5);
        ObjectPoolManager.Instance.WarmPool(sphere, 5);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.C)) {
            ObjectPoolManager.Instance.SpawnObject(cube);
        }

        if (Input.GetKeyDown(KeyCode.S))
        {
            ObjectPoolManager.Instance.SpawnObject(sphere);
        }
    }
}
           

2、Move

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

public class Move : MonoBehaviour
{
    // Start is called before the first frame update
    void OnEnable()
    {
        Invoke("Release", 3);
    }

    // Update is called once per frame
    void Update()
    {

        this.transform.Translate(transform.right * Time.deltaTime * 10);
    }
    void Release()
    {
        ObjectPoolManager.Instance.ReleaseObject(this.gameObject);
    }
}
           

3、ObjectPoolManager

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

public class ObjectPoolManager : MonoSingleton<ObjectPoolManager>
{
    // 是否打印对象池对象使用情况(默认不打印)
    private bool isLogUsedPoolObject = false;

    // 预制体(对象池对象)生成对象池的字典
    private Dictionary<GameObject, ObjectPool<GameObject>> prefabPoolDictinary;

    // 正在使用的对象的字典
    private Dictionary<GameObject, ObjectPool<GameObject>> usedPoolObjectbDictinary;

    // 对象池是否更新使用的标志
    private bool dirty = false;

    protected override void Awake() {
        base.Awake();

        // 初始化字典
        prefabPoolDictinary = new Dictionary<GameObject, ObjectPool<GameObject>>();
        usedPoolObjectbDictinary = new Dictionary<GameObject, ObjectPool<GameObject>>();
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (isLogUsedPoolObject == true && dirty == true)
        {
            PrintUsedPoolObjectStatue();
            dirty = false;
        }
    }

    /// <summary>
    /// 是否打印对象池对象使用情况(默认不打印)
    /// </summary>
    /// <param name="isLogUsedPoolObject"></param>
    public void SetIsLogUsedPoolObject(bool isLogUsedPoolObject) {
        this.isLogUsedPoolObject = isLogUsedPoolObject;
    }

    /// <summary>
    /// 孵化器孵化指定个数对象池对象
    /// </summary>
    /// <param name="prefab">预制体</param>
    /// <param name="count">要预生成对象池对象</param>
    public void WarmPool(GameObject prefab, int count) {
        if (prefabPoolDictinary.ContainsKey(prefab)) {
            Debug.Log("Pool for prefab " + prefab.name + " has already been created");
        }

        ObjectPool<GameObject> pool = new ObjectPool<GameObject>(()=> {
            return InstantiatePrefab(prefab);

        }, count);

        // 添加到字典中
        prefabPoolDictinary[prefab] = pool;

        // 更新使用数据标志
        dirty = true;

    }

    /// <summary>
    /// 从对象池拿出指定对象使用
    /// </summary>
    /// <param name="prefab">要使用的对象</param>
    /// <returns>对象池返回的可用对象</returns>
    public GameObject SpawnObject(GameObject prefab) {
        return SpawnObject(prefab,Vector3.zero,Quaternion.identity);
    }


    public GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
    {
        // 如果该预制体没有孵化,则先进行孵化 1 个
        if (prefabPoolDictinary.ContainsKey(prefab) == false) {
            WarmPool(prefab, 1);
        }

        // 从对象池中获取对象
        ObjectPool<GameObject> pool = prefabPoolDictinary[prefab];         
        GameObject clone = pool.GetObjectPoolContainerItem();

        // 设置对象的位置旋转,显示物体
        clone.transform.position = position;
        clone.transform.rotation = rotation;
        clone.SetActive(true);

        // 把拿出来的对象添加到已使用的字典中
        usedPoolObjectbDictinary.Add(clone, pool);

        // 更新使用数据标志
        dirty = true;

        return clone;
    }

    /// <summary>
    /// 释放使用的对象池对象
    /// </summary>
    /// <param name="clone">对象</param>
    public void ReleaseObject(GameObject clone) {
        clone.SetActive(false);

        // 已使用的字典中
        if (usedPoolObjectbDictinary.ContainsKey(clone))
        {
            usedPoolObjectbDictinary[clone].ReleaseItem(clone);
            usedPoolObjectbDictinary.Remove(clone);

            // 更新使用数据标志
            dirty = true;
        }
        else {

            Debug.Log("No pool contains the object: " + clone.name);

        }
    }

    /// <summary>
    /// 打印吃对象使用情况
    /// </summary>
    private void PrintUsedPoolObjectStatue() {

        foreach (KeyValuePair<GameObject, ObjectPool<GameObject>> keyVal in prefabPoolDictinary)
        {
            Debug.Log(string.Format("Object Pool for Prefab: {0} In Use: {1} Total {2}", keyVal.Key.name, keyVal.Value.CountUsedItems, keyVal.Value.Count));
        }
    }

    /// <summary>
    /// 生成函数,父物体为被物体
    /// </summary>
    /// <param name="prefab">预制体</param>
    /// <returns></returns>
    private GameObject InstantiatePrefab(GameObject prefab)
    {
        var go = Instantiate(prefab) as GameObject;
        go.transform.parent = this.transform;
        go.SetActive(false);
        return go;
    }
}
           

4、ObjectPool

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

/// <summary>
/// 对象池
/// </summary>
public class ObjectPool <T>
{
    // 对象池中的对象列表
    private List<ObjectPoolContainer<T>> listObjects;

    // 对象池中使用了的对象字典
    private Dictionary<T, ObjectPoolContainer<T>> usedObjectDictionary;

    // 回调事件
    private Func<T> factoryFuc;

    // 对象列表索引
    private int lastObjectIndex =0;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="factoryFunc">工厂回调函数</param>
    /// <param name="initialSize">初始化个数</param>
    public ObjectPool(Func<T> factoryFunc, int initialSize)
    {

        this.factoryFuc = factoryFunc;

        listObjects = new List<ObjectPoolContainer<T>>();
        usedObjectDictionary = new Dictionary<T, ObjectPoolContainer<T>>();

        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()
    {
        ObjectPoolContainer<T> container = new ObjectPoolContainer<T>();

        // 生成实例
        container.Item = factoryFuc.Invoke();

        // 实例添加到对象池列表中
        listObjects.Add(container);

        return container;
    }

    /// <summary>
    /// 从对象列中获取可用的对象
    /// </summary>
    /// <returns>返回可用的对象</returns>
    public T GetObjectPoolContainerItem() {
        ObjectPoolContainer<T> container = null;

        for (int i = 0; i < listObjects.Count; i++)
        {
            // 对象列表索引递增,并且防止越界
            lastObjectIndex++;
            lastObjectIndex %= listObjects.Count;

            // 如果列表中的对象正在使用,则进行下一循环,否则返回该对象,并退出循环
            if (listObjects[lastObjectIndex].Used)
            {
                continue;
            }
            else {

                container = listObjects[lastObjectIndex];
                break;
            }
        }

        // 如果没有可用的对象,重新生成一个对象
        if (container == null) {
            container = CreateContainer();
        }

        // 标记当前对象已经被使用,并添加到使用列表中 
        container.Consume();
        usedObjectDictionary.Add(container.Item,container);
        return container.Item;
    }

    /// <summary>
    /// 释放正在使用的对象
    /// </summary>
    /// <param name="item">要释放的对象</param>
    /// <returns>true:释放成功/false: 释放失败</returns>
    public bool ReleaseItem(object item) {
        return ReleaseItem((T)item);
    }

    /// <summary>
    /// 从已使用字典中,释放正在使用的对象
    /// </summary>
    /// <param name="item">释放对象</param>
    /// <returns>true:释放成功/false: 释放失败</returns>
    public bool ReleaseItem(T item) {

        // 判断是否存在已使用对象字典中
        if (usedObjectDictionary.ContainsKey(item))
        {

            // 存在,即释放该对象,并从已使用字典中移除
            ObjectPoolContainer<T> container = usedObjectDictionary[item];
            container.Release();
            usedObjectDictionary.Remove(item);

            return true;
        }
        else {
            Debug.Log("This object pool does not contain the item provided: " + item);

            return false;
        }
    }

    /// <summary>
    /// 获取对象池对象列表中的个数
    /// </summary>
    public int Count {
        get {
            return listObjects.Count;
        }
    }

    /// <summary>
    /// 获取对象池已经使用的对象个数
    /// </summary>
    public int CountUsedItems {
        get {
            return usedObjectDictionary.Count;
        }
    }
}
           

5、ObjectPoolContainer

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

/// <summary>
/// 对象池中的对象使用情况
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObjectPoolContainer<T>
{
    // 获取实例
    private T item;

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

    
    // 是否使用
    public bool Used { get; private set; }

    /// <summary>
    /// 使用该对象池的对象
    /// </summary>
    public void Consume() {
        Used = true;
    }

    /// <summary>
    /// 释放对象池中的对象
    /// </summary>
    public void Release() {

        Used = false;
    }
}
           

6、MonoSingleton

using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance = null;

    private static readonly object locker = new object();

    private static bool bAppQuitting;

    public static T Instance
    {
        get
        {
            if (bAppQuitting)
            {
                instance = null;
                return instance;
            }

            lock (locker)
            {
                if (instance == null)
                {
                    // 保证场景中只有一个 单例
                    T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];
                    if (managers.Length != 0)
                    {
                        if (managers.Length == 1)
                        {
                            instance = managers[0];
                            instance.gameObject.name = typeof(T).Name;
                            return instance;
                        }
                        else
                        {
                            Debug.LogError("Class " + typeof(T).Name + " exists multiple times in violation of singleton pattern. Destroying all copies");
                            foreach (T manager in managers)
                            {
                                Destroy(manager.gameObject);
                            }
                        }
                    }


                    var singleton = new GameObject();
                    instance = singleton.AddComponent<T>();
                    singleton.name = "(singleton)" + typeof(T);
                    singleton.hideFlags = HideFlags.None;
                    DontDestroyOnLoad(singleton);

                }
                instance.hideFlags = HideFlags.None;
                return instance;
            }
        }
    }

    protected virtual void Awake()
    {
        bAppQuitting = false;
    }

    protected virtual void OnDestroy()
    {
        bAppQuitting = true;
    }
}
           

七、参考工程

点击下载工程

八、参考资料

参考的 Github 工程:https://github.com/thefuntastic/unity-object-pool

继续阅读