《Unity3D》通過對象池模式,管理場景中的元素


池管理類有啥用?

在游戲場景中,我們有時候會需要復用一些游戲物體,比如常見的子彈、子彈碰撞類,某些情況下,怪物也可以使用池管理,UI部分比如:血條、文字等等

這些元素共同的特性是:存在固定生命周期,使用比較頻繁,場景中大量使用。

所以,我們就通過池管理思路,在游戲初始化的時候,生成一個初始的池,存放我們要復用的元素,

當要用到時,從池中取出;生命周期結束,放回到池中。

代碼

這個池的參數有兩個:1池中存放的元素 2 池的初始容量(如果池不夠了,則會按照這個容量進行擴展)

代碼如下

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

/// <summary>
/// 通用的池管理類
/// </summary>
public class ObjectPoolManager : MonoBehaviour
{
    #region 公共屬性

    /// <summary>
    /// 池中所使用的元素Prefab
    /// </summary>
    public GameObject ObjPrefab;

    /// <summary>
    /// 初始容量
    /// </summary>
    public int InitialCapacity;

    #endregion

    #region 私有屬性

    /// <summary>
    /// 初始下標
    /// </summary>
    private int _startCapacityIndex;

    /// <summary>
    /// 可用下標
    /// </summary>
    private List<int> _avaliableIndex;

    /// <summary>
    /// 池中全部元素
    /// </summary>
    private Dictionary<int, GameObject> _totalObjList;

    #endregion

    #region 事件/重寫方法

    void Start()
    {
        _avaliableIndex = new List<int>(InitialCapacity);
        _totalObjList = new Dictionary<int, GameObject>(InitialCapacity);
        expandPool();
    }
    #endregion

    #region 公共方法

    /// <summary>
    /// 取得一個物體,返回值 1,obj代表,ID是1的物體被取到,ID可以用來歸還物體的時候用到
    /// </summary>
    /// <returns></returns>
    public KeyValuePair<int, GameObject> PickObj()
    {

        if (_avaliableIndex.Count == 0)
            expandPool();

        int id = _avaliableIndex[0];
        _avaliableIndex.Remove(id);

        _totalObjList[id].SetActive(true);
        return new KeyValuePair<int, GameObject>(id, _totalObjList[id]);
    }

    /// <summary>
    /// 從池中取出元素,在制定時間后回收
    /// </summary>
    /// <param name="existSecond"></param>
    /// <returns></returns>
    public KeyValuePair<int, GameObject> PickObjWithDelayRecyle(float existSecond)
    {
        KeyValuePair<int, GameObject> obj = PickObj();
        StartCoroutine(startRecycleExplosion(obj.Key, existSecond));
        return obj;
    }


    /// <summary>
    /// 回收一個物體
    /// </summary>
    /// <param name="id"></param>
    public void RecyleObj(int id)
    {
        _totalObjList[id].SetActive(false);
        _totalObjList[id].transform.parent = transform;
        _avaliableIndex.Add(id);
    }

    #endregion

    #region 私有方法

    IEnumerator startRecycleExplosion(int id, float waitTime)
    {
        yield return new WaitForSeconds(waitTime);
        RecyleObj(id);
    }

    /// <summary>
    /// 擴展池
    /// </summary>
    private void expandPool()
    {
        int start = _startCapacityIndex;
        int end = _startCapacityIndex + InitialCapacity;

        for (int i = start; i < end; i++)
        {
            //加入驗證判斷,避免在多個請求同時觸發擴展池需求
            if (_totalObjList.ContainsKey(i))
                continue;

            GameObject newObj = Instantiate(ObjPrefab) as GameObject;
            newObj.SetActive(false);
            _avaliableIndex.Add(i);
            _totalObjList.Add(i, newObj);
        }
        _startCapacityIndex = end;
    }
    #endregion
}

 

值得注意的是:放回池中的時候,我們把元素的父節點也設置為池元素,這樣做是避免當元素掛載的對象在內存中被刪除的時候,元素也被刪除的問題。

調用

 將本類掛載到場景中的某個GameObject上,在U3D編輯界面進行參數的賦值,就可以再游戲中訪問了。

聲明方式:

 ObjectPoolManager _bulletPool = GameObject.Find("你掛載的物體名稱").GetComponent<ObjectPoolManager>();

取出元素:

  KeyValuePair<int, GameObject> bulletKV = _bulletPool.PickObj();

放回:

 _bulletPool.RecyleObj(bulletID);

 

在本人的實際使用中,用這個類管理了

  1. 子彈
  2. 子彈碰撞特效
  3. 攻擊特效
  4. 被攻擊特效
  5. 怪物血條(基於NGUI制作)
  6. 場景部分文字(施法時、怪物被攻擊時,基於NGUI制作)
  7. 等等。。

歡迎各位進行討論


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM