2015年6月13日 星期六

【Unity3D】PoolManager 物件池 - Unity3D開發日誌

*測試環境:Visual Studio 2013 Community、Unity3D 4.6.5f、UnityVS 1.9.9
*更新日期2015/06/13

物件池是一個有效率且節省資源的用法。這個範例是一個簡易的物件池系統,並有參數可以控制。

        他可以在載入場景時就預先實體化物件,而不是在需要實體化物件時才Instantiate,這會造成在實體物件時導致系統要與顯示卡同步所產生的LAG情況。

        也可以減少物件一直被Destory再次Instantiate物件。在GC還沒啟動時,持續造成記憶體肥大與效能上的浪費。



使用方法與配置圖




1.ObjectDeck:存放要被實體化的物件(Perfab)
2.Pool:存放物件池的位置(一定要指定)
3-1.初始數量:載入場景就會實體化的固定數量。
3-2.清理上限:達到這個上限值物件池將會清理物件。
3-3.清理時間:等待X秒,並達到物件池清理上限,才會清理。
3-4.保留數量:物件池清理後,會保留下來的物件數量。
4.SpawnHere:這是要產生的位置。可以自行任意配置,不一定要照本篇這樣使用。


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

/* ***************************************************************
 * -----Copyright © 2015 Gansol Studio.  All Rights Reserved.-----
 * -----------            CC BY-NC-SA 4.0            -------------
 * -----------  @Website:  EasyUnity@blogspot.com    -------------
 * -----------  @Email:    GansolTW@gmail.com        -------------
 * -----------  @Author:   Krola.                    -------------
 * ***************************************************************
 *                          Description
 * ***************************************************************
 * 
 * 物件池 提供一個高效率的物件生成、回收方式
 * 支援多種物件的物件池
 * 每種物件的數量控制是固定的(個別不同數量請自行修改)
 * 
 * ***************************************************************/

public class PoolManager : MonoBehaviour
{
    private Dictionary<string, object> _tmpDict;
    private Dictionary<int, string> _dictObject;

    private GameObject clone;
    private byte objectID;          // 取得的ID 須自行改寫
    private string objectName;      // 取得的 物件名稱

    private float _lastTime;
    private float _currentTime;

    [Tooltip("物件池位置")]
    public GameObject Pool;
    [Tooltip("物件匣")]
    public GameObject[] ObjectDeck;
    [Tooltip("產生數量")]
    public int spawnCount = 5;
    [Tooltip("物件池上限(各種類分開)")]
    public int clearLimit = 5;
    [Tooltip("物件池上限(各種類分開)")]
    public int clearTime = 10;
    [Tooltip("物件池保留量(各種類分開)")]
    public int reserveCount = 2;

    private bool _poolingFlag = false;        // 初始化物件池
    public bool poolingFlag { get { return _poolingFlag; } }

    void Awake()
    {
        _lastTime = 0;
        _currentTime = 0;
        clearTime = 10;
        _poolingFlag = false;      // 初始化物件池
        _dictObject = new Dictionary<int, string>();
        _tmpDict = new Dictionary<string, object>();

        Debug.Log(ObjectDeck.Length);
        // 加入字典
        for (int item=0;item < ObjectDeck.Length;item++)
        {
            _dictObject.Add(item, ObjectDeck[item].name);
        }


        // 生出 預設數量的物件
        foreach (KeyValuePair<int, string> item in _dictObject)
        {
            clone = new GameObject();

            clone.name = item.Value;
            clone.transform.parent = Pool.transform;
            clone.layer = clone.transform.parent.gameObject.layer;
            clone.transform.localScale = Vector3.one;

            for (int i = 0; i < spawnCount; i++)
            {
                clone = (GameObject)Instantiate(ObjectDeck[item.Key]);   // 等傳老鼠ID名稱近來這要改
                clone.name = item.Value;
                clone.transform.parent = Pool.transform.FindChild(item.Value).transform;
                clone.transform.localScale = Vector3.one;
                clone.transform.gameObject.SetActive(false);    // 新版 子物件隱藏
            }
        }
         Debug.Log("pooling Completed ! ");
        _poolingFlag = true;
    }


    // 每一次顯示一個GameObject。如果GameObject不足,Spawn一個物件並顯示。
    // 回傳 ( GameObject / null )

    public GameObject ActiveObject(int objectID)
    {
        _dictObject.TryGetValue(objectID, out objectName);//等傳老鼠ID名稱近來這要改miceName

        if (Pool.transform.FindChild(objectName).childCount == 0)
        {
            clone = (GameObject)Instantiate(ObjectDeck[objectID], Vector3.zero, Quaternion.identity);
            clone.name = objectName;
            clone.transform.parent = Pool.transform.FindChild(objectName).transform;
            clone.transform.localScale = Vector3.one;
            return clone;
        }

        for (int i = 0; i < Pool.transform.FindChild(objectName).childCount; i++)
        {
            GameObject obj;

            obj = Pool.transform.FindChild(objectName).GetChild(i).gameObject;

            if (obj.name == objectName && !obj.transform.gameObject.activeSelf)
            {
                obj.transform.gameObject.SetActive(true);
                return obj;
            }
        }
        return null;
    }


    // 每一次顯示一個GameObject。如果GameObject不足,Spawn一個物件並顯示。
    //回傳 ( GameObject / null )

    public GameObject ActiveObject(string objectName)
    {
        int objectID = _dictObject.FirstOrDefault(x => x.Value == objectName).Key;       // 找Key

        if (Pool.transform.FindChild(objectName).childCount == 0)
        {
            clone = (GameObject)Instantiate(ObjectDeck[objectID], Vector3.zero, Quaternion.identity);
            clone.name = objectName;
            clone.transform.parent = Pool.transform.FindChild(objectName).transform;
            clone.transform.localScale = Vector3.one;
            return clone;
        }

        for (int i = 0; i < Pool.transform.FindChild(objectName).childCount; i++)
        {
            GameObject obj;

            obj = Pool.transform.FindChild(objectName).GetChild(i).gameObject;

            if (obj.name == objectName && !obj.transform.gameObject.activeSelf)
            {
                obj.transform.gameObject.SetActive(true);
                return obj;
            }
        }
        return null;
    }

    void Update()
    {
        _currentTime = Time.time;

        if (_currentTime - _lastTime > clearTime)     // 達到清除時間時
        {
            for (int i = 0; i < Pool.transform.childCount; i++)       // 跑遍動態池
            {
                if (Pool.transform.GetChild(i).childCount > clearLimit)           // 如果動態池超過限制數量
                {
                    for (int j = 0; j < Pool.transform.GetChild(i).childCount - reserveCount; j++)    // 銷毀物件
                    {
                        Destroy(Pool.transform.GetChild(i).GetChild(j).gameObject);
                    }
                }
            }
            _lastTime = _currentTime;
        }
    }
}




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


public class TEST : MonoBehaviour
{
    public GameObject spawnHere;        // 這是要把物件 Active的位置(你可以放到任何地方)

    PoolManager poolManager;
    GameObject clone;                   // 暫存clone物件
    GameObject[] tmpObject;             // 暫存物件

    bool flag;


    void Start()
    {
        flag = true;
        tmpObject = new GameObject[10];
        poolManager = GetComponent<PoolManager>();
    }

    void Update()
    {
        if (poolManager.poolingFlag && flag)     // 如果物件池 初始化完成  (注意:poolManager.poolingFlag = true 時才可以使用物件池)
        {
            flag = false;
            Debug.Log("Active Object!");

            clone = (GameObject)poolManager.ActiveObject(0); // 啟動物件Cat
            clone.transform.parent = spawnHere.transform;
            clone.transform.localPosition = Vector3.zero;

            // 生10個會超過物件池基本數量 就會在實體化新的物件出來
            for (int i = 0; i < 10; i++)
            {
                clone = (GameObject)poolManager.ActiveObject(0); // 啟動物件Cat
                clone.transform.parent = spawnHere.transform;
                clone.transform.localPosition =new  Vector3(i,i,i);
                tmpObject[i] = clone;   // 暫存物件 等等用來示範丟回物件池
            }

            clone = (GameObject)poolManager.ActiveObject(1); // 啟動物件Rabbit
            clone.transform.parent = spawnHere.transform;
            clone.transform.localPosition = Vector3.one;
        }
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 100, 100), "Recover"))
        {
            for (int i = 0; i < 10; i++)
            {
                clone = tmpObject[i];
                clone.transform.parent = poolManager.Pool.transform.FindChild(clone.name);  // 把物件 丟回物件池
                clone.SetActive(false);                                                     // 並讓他看不見
            }
            // 經過 一段時間(可自訂) 物件池會清理掉多的物件 並保留(可自訂數量) 的物件
        }
    }
}

0 ♥:

張貼留言