Unity フレームワークの学習 - オブジェクト プール

       オブジェクトの作成と破棄を頻繁に行うと、パフォーマンスのオーバーヘッドが発生します。
        オブジェクトが作成されると、システムはそのオブジェクト用に新しいスペースを開きます。オブジェクトが破棄されると、そのオブジェクトはメモリ ガベージになります。メモリ ガベージが一定のレベルに達すると、ガベージ コレクション メカニズムがトリガされて、メモリ ガベージがクリーンアップされます。このとき、ガベージはクリーンアップされているため、プログラムが異常終了する可能性があります。立ち往生。

        この問題を改善するには、オブジェクト プーリングを使用します。これを使用すると、プログラムのパフォーマンスが向上し、簡単に動かなくなることがあります。

        オブジェクト プールの原理:


        1. オブジェクトを作成する場合は、直接作成するのではなく、まずオブジェクト プール内でその種類のオブジェクトを探し、オブジェクト プール内にその種類のオブジェクトがある場合は、オブジェクト プールから取り出して使用します。このオブジェクトは、オブジェクト プールにこのタイプのオブジェクトが存在しない場合にのみ作成されます。
        2. オブジェクトを破棄する場合は、直接破棄せず、オブジェクトをオブジェクト プールに入れ、次回オブジェクトを作成するときに使用できるように保存します。

プレハブはオブジェクト プールに対応し、コードは次のとおりです。

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

/// <summary>
/// 对象池
/// </summary>
public class ObjectPool : MonoBehaviour
{
    public GameObject prefabs;   //这个对象池存储的游戏对象的预制体

    public int capcity = 100;  //对象池的容量

    //从这个对象池中取出来正在使用的游戏对象
    public List<GameObject> usedGameObjectList = new List<GameObject>();

    //存在这个对象池中没有被使用的游戏对象
    public List<GameObject> unusedGameObjectList = new List<GameObject>();

    //这个池子总共拥有的游戏对象的个数
    public int TotalGameObjectCount { get => usedGameObjectList.Count + unusedGameObjectList.Count; }

    /// <summary>
    /// 从对象池中获取一个对象,并返回一个对象
    /// </summary>
    public GameObject Spawn(Vector3 position,Quaternion rotation,Transform parent=null)
    {
        GameObject go;

        //池子中有
        if (unusedGameObjectList.Count > 0)
        {
            go = unusedGameObjectList[0];
            unusedGameObjectList.RemoveAt(0);
            usedGameObjectList.Add(go);
            go.transform.localPosition = position;
            go.transform.localRotation = rotation;
            go.transform.SetParent(parent, false);
            go.SetActive(true);
        }
        else  //对象池中没有,实例化
        {
            go = Instantiate(prefabs, position, rotation, parent);
            usedGameObjectList.Add(go);
        }

        //如果该游戏对象身上继承MonoBehaviour的脚本中写了名叫OnSpwan的方法,则会执行它们一次
        //                                           就算没有接收者也不报错
        go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);

        return go;
    }

    //回收进对象池中
    public void DesPawn(GameObject go)
    {
        if (go == null) return;

        //遍历这个对象池中所有正在使用的游戏对象
        for (int i = 0; i < usedGameObjectList.Count; i++)
        {
            if (usedGameObjectList[i] == go)
            {
                //如果这个对象池的容量不为负数,且容纳的游戏对象已经满了,则把0号游戏对象删除掉
                //确保之后新的游戏对象能放入池子中
                if (capcity >= 0 && usedGameObjectList.Count >= capcity)
                {
                    if (unusedGameObjectList.Count>0)
                    {
                        DesPawn(unusedGameObjectList[0]);
                        unusedGameObjectList.RemoveAt(0);

                    }
                }

                //把游戏对象回收到对象池中
                unusedGameObjectList.Add(go);
                usedGameObjectList.RemoveAt(i);

                //如果该游戏对象身上继承MonoBehaviour的脚本写了名叫OnDespawn的方法,则在回收的时候会执行一次
                //这个方法用来在回收前将游戏对象还原
                go.SendMessage("OnDespawn", SendMessageOptions.DontRequireReceiver);

                go.SetActive(false);
                go.transform.SetParent(transform, false);  //取消子物体

                return;
            }
        }
    }

    /// <summary>
    /// 把通过这个对象池生成的所有对象全部隐藏并放入对象池中
    /// </summary>
    public void DesPawnAll()
    {
        int count = usedGameObjectList.Count;

        for (int i = 1; i <= count; i++)
        {
            DesPawn(usedGameObjectList[0]);
        }

        usedGameObjectList.Clear();
    }

    /// <summary>
    /// 预加载的方法,预先在池子里放几个对象
    /// </summary>
    public void PreLoad(int amount = 1)
    {
        if (prefabs == null) return;

        if (amount <= 0) return;

        for (int i = 1; i <= amount ; i++)
        {
            GameObject go = Instantiate(prefabs, Vector3.zero, Quaternion.identity);
            go.SetActive(false);
            go.transform.SetParent(transform, false);

            unusedGameObjectList.Add(go);
            go.name = prefabs.name;  //更改一下生成的物体的名字
           
        }

    }

}

1 つのプレハブと 1 つのプール、および大きなプールがすべての小さなオブジェクト プールを管理します

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

/// <summary>
/// 对象池管理,可以通过这个管理器从对象池生成游戏对象,也可以回收游戏对象进对象池
/// </summary>
public class ObjectPoolsManager : SingletonPatternBase<ObjectPoolsManager>
{
    GameObject poolsParent;  //所有对象池的父物体
    string poolsParentName = "ObjectPools";  //大对象池的名字

    //当前所有小对象池的列表
    public List<ObjectPool> objectPoolsList = new List<ObjectPool>();

    public Dictionary<GameObject, ObjectPool> objectDictionary = new Dictionary<GameObject, ObjectPool>();

    /// <summary>
    /// 从对象池生成游戏对象
    /// 如果对象池有,从对象池中取出来用
    /// 如果对象池没有,实例化该对象
    /// </summary>
    public GameObject Spawn(GameObject prefab,Vector3 position,Quaternion rotation,Transform parent=null)
    {
        if (prefab == null) return null;

        //如果场景中没有对象池的父物体,则生成一个空物体作为所有对象池的父物体
        CreatePoolsParentIfNull();

        //先通过预制体查找它所属的小对象池,如果找到了,则返回这个小对象池
        //如果找不到,则创建一个小对象池,用来存放这种预制体
        ObjectPool objectPool = FindPoolByPrefabOrCreatePool(prefab);

        GameObject go = objectPool.Spawn(position, rotation, parent);

        objectDictionary.Add(go, objectPool);

        return go;
    }

    /// <summary>
    /// 回收的方法
    /// </summary>
    public void DesPawn(GameObject go,float delayTime=0)
    {
        if (go == null) return;

        MonoManager.Instance.StartCoroutine(DespawnCoroutine(go, delayTime));
    }

    IEnumerator DespawnCoroutine(GameObject go, float delayTime = 0)
    {
        //等待指定的秒数
        yield return new WaitForSeconds(delayTime);

        if (objectDictionary.TryGetValue(go, out ObjectPool pool))
        {
            objectDictionary.Remove(go);
            pool.DesPawn(go);
        }
        else
        {
            //获取这个游戏对象所属的对象池
            pool = FindPoolByUsedGameObject(go);

            if (pool != null)
            {
                pool.DesPawn(go);
            }
        }
    }

    /// <summary>
    /// 通过正在使用的物体获取这个游戏对象所属的对象池
    /// </summary>
    ObjectPool FindPoolByUsedGameObject(GameObject go)
    {
        if (go == null) return null;

        for (int i = 0; i < objectPoolsList.Count; i++)
        {
            ObjectPool pool = objectPoolsList[i];

            for (int j = 0; j < pool.usedGameObjectList.Count; j++)
            {
                if (pool.usedGameObjectList[j]==go)
                {
                    return pool;
                }
            }
        }
        return null;
    }


    /// <summary>
    /// 如果场景中没有对象池的父物体,则生成一个空物体作为所有对象池的父物体
    /// </summary>
    void CreatePoolsParentIfNull()
    {
        if (poolsParent == null)
        {
            objectPoolsList.Clear();
            objectDictionary.Clear();

            poolsParent = new GameObject(poolsParentName);
        }
    }

    /// <summary>
    /// 先通过预制体查找它所属的小对象池,如果找到了,则返回这个小对象池
    /// 如果找不到,则创建一个小对象池,用来存放这种预制体
    /// </summary>
    /// <returns></returns>
    ObjectPool FindPoolByPrefabOrCreatePool(GameObject prefab)
    {
        //确保大对象池存在
        CreatePoolsParentIfNull();

        //查找并返回该预制体对象的对象池
        ObjectPool objectPool = FindPoolByPrefab(prefab);

        if (objectPool == null)
        {
            objectPool = new GameObject($"ObjectPool{prefab.name}").AddComponent<ObjectPool>();
            objectPool.prefabs = prefab;
            objectPool.transform.SetParent(poolsParent.transform);
            objectPoolsList.Add(objectPool);
        }
        return objectPool;
    }

    /// <summary>
    /// 查找并返回该预制体对象的对象池
    /// </summary>
    ObjectPool FindPoolByPrefab(GameObject prefab)
    {
        if (prefab == null) return null;

        for (int i = 0; i < objectPoolsList.Count; i++)
        {
            if (objectPoolsList[i].prefabs == prefab)
            {
                return objectPoolsList[i];
            }
        }

        return null;
    }
}

オブジェクトにこのようなメソッドを記述して、オブジェクトの状態を初期化し、ダーティ データをクリアするのが最善です。

void OnDespawn()
    {         transform.position = Vector3.zero;         GetComponent<Rigidbody>().velocity = Vector3.zero;     }


 

おすすめ

転載: blog.csdn.net/zaizai1007/article/details/132365705