UFL3-3——对象上限优化

对象上限优化

目前我们制作的缓存池模块,理论上来说,当动态创建的对象长时间不放回抽屉
每次从缓存池中动态获取对象时,会不停的新建对象,那么也就是对象的数量是没有上限的
场景上的某种对象可以存在n个

而对象上限优化指的就是,我们希望控制对象数量有上限,
对于不重要的资源我们没必要让其无限加量,而是将“使用最久”的资源直接抢来用

主要目的:
更加彻底的复用资源,对对象的数量上限加以限制
可以优化内存空间,甚至优化性能(减少数量上限,可以减小渲染压力)

制作思路和具体实现

  1. 在抽屉里声明一个容器usedList​用来记录正在使用的资源,
    并开放一个属性UsedCount​用于获取正在使用的资源的数量,开放一个方法PushUsedList​用于将资源放入到usedList​内

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class PoolData
    {
    //抽屉根对象,用来就行布局管理的对象
    private GameObject poolObj;
    //用来存储抽屉中的对象,记录没有使用的对象
    private Stack<GameObject> dataStack = new Stack<GameObject>();
    //记录使用中的对象的列表
    private List<GameObject> usedList = new List<GameObject>();
    //获取容器中是否有对象
    public int Count => dataStack.Count;

    //获取使用中的对象的数量
    public int UsedCount => usedList.Count;

    public PoolData(GameObject poolRootObj, string poolName); //..

    // 从抽屉中弹出数据对象
    public GameObject Pop(); //..

    // 将数据压入到抽屉内
    public void Push(GameObject obj); // ..

    /// <summary>
    /// 将对象添加到使用中的列表
    /// </summary>
    /// <param name="obj">要添加到使用中列表的对象</param>
    public void PushUsedList(GameObject obj)
    {
    usedList.Add(obj);
    }
    }
  2. 每次获取对象GetObj​时,传入一个抽屉最大容量值maxNum​(可以给一个默认值)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class PoolManager : BaseManager<PoolManager>
    {
    //柜子容器当中有抽屉的体现
    private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();

    //池子根对象
    private GameObject poolObj;

    //是否开启Hierarchy窗口自动布局功能
    public static bool isOpenLayout = true;

    private PoolManager() { }

    // 拿东西的方法
    public GameObject GetObj(string name, int maxNum = 50)
    {
    GameObject obj;
    // TODO.. 根据maxNum,PoolData的是否存在,以及所有中的对象的数量来决定如何取对象
    return obj;
    }

    // 往缓存池中放入对象
    public void PushObj(GameObject obj); //..
    // 清除整个柜子当中的数据
    public void ClearPool(); //..
    }
  3. 从缓存池中获取对象GetObj​时就需要创建抽屉PoolData​,用于记录当前使用着的对象

    由于总是是先获取GetObj​后压入PushObj​,因此PushObj​内不再需要检查抽屉是否存在,只需要向抽屉压入对象即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class PoolManager : BaseManager<PoolManager>
    {
    //柜子容器当中有抽屉的体现
    private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();

    //池子根对象
    private GameObject poolObj;

    //是否开启Hierarchy窗口自动布局功能
    public static bool isOpenLayout = true;

    private PoolManager() { }

    // 拿东西的方法
    public GameObject GetObj(string name, int maxNum = 50)
    {
    GameObject obj;
    // TODO.. 根据maxNum,PoolData的是否存在,以及所有中的对象的数量来决定如何取对象

    return obj;
    }

    // 往缓存池中放入对象
    public void PushObj(GameObject obj)
    {
    //如果根物体为空且自动排布开启,就创建
    if (poolObj == null && isOpenLayout)
    poolObj = new GameObject("Pool");

    poolDic[obj.name].Push(obj);
    }

    // 清除整个柜子当中的数据
    public void ClearPool(); //..
    }
  4. 每次取对象GetObj​时应该分情况考虑

    • 情况1:没有抽屉时

      需要创建一个抽屉PoolData​和一个对象,抽屉添加到poolDic​内,
      这个对象同时需要压入到抽屉的使用中对象的列表usedList​内

      当创建一个抽屉PoolData​时,往往还实例化了一个新的对象,因此在实例化PoolData​的时候,应该同时将对象压入到usedList​内

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      public class PoolData
      {
      //抽屉根对象,用来就行布局管理的对象
      private GameObject poolObj;
      //用来存储抽屉中的对象,记录没有使用的对象
      private Stack<GameObject> dataStack = new Stack<GameObject>();
      //记录使用中的对象的列表
      private List<GameObject> usedList = new List<GameObject>();
      //获取容器中是否有对象
      public int Count => dataStack.Count;

      public int UsedCount => usedList.Count;

      public PoolData(GameObject poolRootObj, string poolName, GameObject usedObj)
      {
      //开启功能时,才会动态创建父子关系
      if (PoolManager.isOpenLayout)
      {
      //创建抽屉父对象,和柜子父对象建立父子关系
      poolObj = new GameObject(poolName);
      poolObj.transform.SetParent(poolRootObj.transform);
      }
      //创建抽屉时,外部肯定是会动态创建一个对象的,我们应该将其记录到使用中的对象容器中
      PushUsedList(usedObj);
      }

      // 从抽屉中弹出数据对象
      public GameObject Pop(); //..

      // 将数据压入到抽屉内
      public void Push(GameObject obj); //..

      /// <summary>
      /// 将对象添加到使用中的列表
      /// </summary>
      /// <param name="obj">要添加到使用中列表的对象</param>
      public void PushUsedList(GameObject obj)
      {
      usedList.Add(obj);
      }
      }
    • 情况2:有抽屉,并且 抽屉里有没用的对象 或者 使用中对象超过上限时

      调用PoolData​的Pop​方法取出对象,
      其中,当使用中对象超过上限时,Pop​方法执行的是从usedList​内取出最早放入的对象,并重新放入到usedList​内的逻辑
      当抽屉里有没用的对象时,Pop​方法直接从dataStack​内取出一个对象激活即可

      因此PoolData​的Pop​方法需要添加一个当抽屉内没有没用的对象时需要执行的分支:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      public class PoolData
      {
      //抽屉根对象,用来就行布局管理的对象
      private GameObject poolObj;
      //用来存储抽屉中的对象,记录没有使用的对象
      private Stack<GameObject> dataStack = new Stack<GameObject>();
      //记录使用中的对象的列表
      private List<GameObject> usedList = new List<GameObject>();
      //获取容器中是否有对象
      public int Count => dataStack.Count;

      public int UsedCount => usedList.Count;

      public PoolData(GameObject poolRootObj, string poolName, GameObject usedObj); //..

      /// <summary>
      /// 从抽屉中弹出数据对象
      /// </summary>
      /// <returns>弹出的数据对象</returns>
      public GameObject Pop()
      {
      //从抽屉内取出并激活对象
      GameObject obj;
      //如果有未使用的对象,就取出这个对象
      if (Count > 0)
      {
      obj = dataStack.Pop();
      usedList.Add(obj); //将取出的对象压入到使用中的列表内
      }
      //如果没有未使用的对象,就需要从正在使用的对象列表内抢来一个最早使用的
      else
      {
      obj = usedList[0]; //取0索引的对象,它就是使用时间最长的对象
      usedList.RemoveAt(0); //并将其从使用中的列表中移除出去
      usedList.Add(obj); //由于该对象还需要使用,所以将其移到列表的最尾部使用
      }

      obj.SetActive(true);
      //开启布局功能时,才需要断开父子关系
      if (PoolManager.isOpenLayout)
      obj.transform.SetParent(null);
      return obj;
      }

      // 将数据压入到抽屉内
      public void Push(GameObject obj); //..

      /// <summary>
      /// 将对象添加到使用中的列表
      /// </summary>
      /// <param name="obj">要添加到使用中列表的对象</param>
      public void PushUsedList(GameObject obj)
      {
      usedList.Add(obj);
      }
      }

    • 情况3:有抽屉,但是抽屉里没有对象,使用中对象也没有超过上限时

      实例化一个对象出来,并将这个对象添加到抽屉PoolData​的usedList​内

    最终PoolManager​的GetObj​修改为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    // 缓存池模块 管理器
    public class PoolManager : BaseManager<PoolManager>
    {
    //柜子容器当中有抽屉的体现
    private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();

    //池子根对象
    private GameObject poolObj;

    //是否开启Hierarchy窗口自动布局功能
    public static bool isOpenLayout = true;

    private PoolManager() { }

    /// <summary>
    /// 拿东西的方法
    /// </summary>
    /// <param name="name">抽屉容器的名字</param>
    /// <returns>从缓存池中取出的对象</returns>
    public GameObject GetObj(string name, int maxNum = 50)
    {
    GameObject obj;
    //无抽屉,或者抽屉无对象且使用中的对象也未超上限,需要新实例化一个对象
    if (!poolDic.ContainsKey(name) ||
    (poolDic[name].Count == 0 && poolDic[name].UsedCount < maxNum))
    {
    obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
    obj.name = name;
    //无抽屉时,创建一个抽屉并将新对象传入到构造函数内
    if (!poolDic.ContainsKey(name))
    poolDic.Add(name, new PoolData(poolObj, name, obj));
    //有抽屉时,将新创建的对象传入到使用中的列表内
    else
    poolDic[name].PushUsedList(obj);
    }
    //当抽屉有对象或者使用中的对象超上限了,就直接从抽屉内取出来使用
    else // if (poolDic[name].Count > 0 || poolDic[name].UsedCount >= maxNum)
    {
    obj = poolDic[name].Pop();
    }
    return obj;
    }

    // 往缓存池中放入对象
    public void PushObj(GameObject obj); //..

    // 清除整个柜子当中的数据
    public void ClearPool(); //..
    }
  5. 每次放回对象时

    由于记录了正在使用的资源,因此每次放入抽屉时还需要从记录容器中移除对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class PoolData
    {
    //抽屉根对象,用来就行布局管理的对象
    private GameObject poolObj;
    //用来存储抽屉中的对象,记录没有使用的对象
    private Stack<GameObject> dataStack = new Stack<GameObject>();
    //记录使用中的对象的列表
    private List<GameObject> usedList = new List<GameObject>();
    //获取容器中是否有对象
    public int Count => dataStack.Count;

    public int UsedCount => usedList.Count;

    public PoolData(GameObject poolRootObj, string poolName, GameObject usedObj); //..

    // 从抽屉中弹出数据对象
    public GameObject Pop();

    /// <summary>
    /// 将数据压入到抽屉内
    /// </summary>
    /// <param name="obj">压入到抽屉内</param>
    public void Push(GameObject obj)
    {
    //失活后压入到抽屉内
    obj.SetActive(false);
    //开启了布局功能,才需要将要压入的对象设置父子关系
    if (PoolManager.isOpenLayout)
    obj.transform.SetParent(poolObj.transform);
    dataStack.Push(obj);
    //这个对象不再使用了,因此需要从记录容器中移除出去
    usedList.Remove(obj);
    }

    // 将对象添加到使用中的列表
    public void PushUsedList(GameObject obj); //..
    }

值得一提的是,按照上述思路实现了代码后,在Hierarchy窗口布局优化功能内会存在一个Bug需要我们去修复

由于我们优化中加入了上限判断,并且在GetObj​时,就会去new PoolData​,
当开启布局优化功能时,由于此时Pool​根对象没有创建,会报空,因为原本Pool​根对象的创建逻辑是在PushObj​中实现的
因此我们需要将原本在PushObj​中创建Pool​根对象的判断,移动到GetObj​中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//取东西的方法
public GameObject GetObj(string name, int maxNum = 50)
{
//如果根物体为空且自动排布开启,就创建
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");

GameObject obj;
//无抽屉,或者抽屉无对象且使用中的对象也未超上限,需要新实例化一个对象
if (!poolDic.ContainsKey(name) ||
(poolDic[name].Count == 0 && poolDic[name].UsedCount < maxNum))
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
obj.name = name;
//无抽屉时,创建一个抽屉并将新对象传入到构造函数内
if (!poolDic.ContainsKey(name))
poolDic.Add(name, new PoolData(poolObj, name, obj));
//有抽屉时,将新创建的对象传入到使用中的列表内
else
poolDic[name].PushUsedList(obj);
}
//当抽屉有对象或者使用中的对象超上限了,就直接从抽屉内取出来使用
else // if (poolDic[name].Count > 0 || poolDic[name].UsedCount >= maxNum)
{
obj = poolDic[name].Pop();
}
return obj;
}

// 往缓存池中放入对象
public void PushObj(GameObject obj)
{
//如果根物体为空且自动排布开启,就创建
//if (poolObj == null && isOpenLayout)
// poolObj = new GameObject("Pool");

poolDic[obj.name].Push(obj);
}
  • 至此,缓存池相关代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class PoolData
    {
    //抽屉根对象,用来就行布局管理的对象
    private GameObject poolObj;
    //用来存储抽屉中的对象,记录没有使用的对象
    private Stack<GameObject> dataStack = new Stack<GameObject>();
    //记录使用中的对象的列表
    private List<GameObject> usedList = new List<GameObject>();
    //获取容器中是否有对象
    public int Count => dataStack.Count;

    public int UsedCount => usedList.Count;

    public PoolData(GameObject poolRootObj, string poolName, GameObject usedObj)
    {
    //开启功能时,才会动态创建父子关系
    if (PoolManager.isOpenLayout)
    {
    //创建抽屉父对象,和柜子父对象建立父子关系
    poolObj = new GameObject(poolName);
    poolObj.transform.SetParent(poolRootObj.transform);
    }
    //创建抽屉时,外部肯定是会动态创建一个对象的,我们应该将其记录到使用中的对象容器中
    PushUsedList(usedObj);
    }

    /// <summary>
    /// 从抽屉中弹出数据对象
    /// </summary>
    /// <returns>弹出的数据对象</returns>
    public GameObject Pop()
    {
    //从抽屉内取出并激活对象
    GameObject obj;
    if (Count > 0)
    {
    obj = dataStack.Pop();
    usedList.Add(obj); //将取出的对象压入到使用中的列表内
    }
    else
    {
    obj = usedList[0]; //取0索引的对象,它就是使用时间最长的对象
    usedList.RemoveAt(0); //并将其从使用中的列表中移除出去
    usedList.Add(obj); //由于该对象还需要使用,所以将其移到列表的最尾部使用
    }

    obj.SetActive(true);
    //开启布局功能时,才需要断开父子关系
    if (PoolManager.isOpenLayout)
    obj.transform.SetParent(null);
    return obj;
    }

    /// <summary>
    /// 将数据压入到抽屉内
    /// </summary>
    /// <param name="obj">压入到抽屉内</param>
    public void Push(GameObject obj)
    {
    //失活后压入到抽屉内
    obj.SetActive(false);
    //开启了布局功能,才需要将要压入的对象设置父子关系
    if (PoolManager.isOpenLayout)
    obj.transform.SetParent(poolObj.transform);
    dataStack.Push(obj);
    //这个对象不再使用了,因此需要从记录容器中移除出去
    usedList.Remove(obj);
    }

    /// <summary>
    /// 将对象添加到使用中的列表
    /// </summary>
    /// <param name="obj">要添加到使用中列表的对象</param>
    public void PushUsedList(GameObject obj)
    {
    usedList.Add(obj);
    }
    }

    /// <summary>
    /// 缓存池模块 管理器
    /// </summary>
    public class PoolManager : BaseManager<PoolManager>
    {
    //柜子容器当中有抽屉的体现
    private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();

    //池子根对象
    private GameObject poolObj;

    //是否开启Hierarchy窗口自动布局功能
    public static bool isOpenLayout = false;

    private PoolManager() { }

    /// <summary>
    /// 拿东西的方法
    /// </summary>
    /// <param name="name">抽屉容器的名字</param>
    /// <returns>从缓存池中取出的对象</returns>
    public GameObject GetObj(string name, int maxNum = 50)
    {
    //如果根物体为空且自动排布开启,就创建
    if (poolObj == null && isOpenLayout)
    poolObj = new GameObject("Pool");

    GameObject obj;
    //无抽屉,或者抽屉无对象且使用中的对象也未超上限,需要新实例化一个对象
    if (!poolDic.ContainsKey(name) ||
    (poolDic[name].Count == 0 && poolDic[name].UsedCount < maxNum))
    {
    obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
    obj.name = name;
    //无抽屉时,创建一个抽屉并将新对象传入到构造函数内
    if (!poolDic.ContainsKey(name))
    poolDic.Add(name, new PoolData(poolObj, name, obj));
    //有抽屉时,将新创建的对象传入到使用中的列表内
    else
    poolDic[name].PushUsedList(obj);
    }
    //当抽屉有对象或者使用中的对象超上限了,就直接从抽屉内取出来使用
    else // if (poolDic[name].Count > 0 || poolDic[name].UsedCount >= maxNum)
    {
    obj = poolDic[name].Pop();
    }
    return obj;
    }

    /// <summary>
    /// 往缓存池中放入对象
    /// </summary>
    /// <param name="name">抽屉(对象池)的名字</param>
    /// <param name="obj">要放入的对象</param>
    public void PushObj(GameObject obj)
    {
    ////如果根物体为空且自动排布开启,就创建
    //if (poolObj == null && isOpenLayout)
    // poolObj = new GameObject("Pool");

    poolDic[obj.name].Push(obj);
    }

    /// <summary>
    /// 清除整个柜子当中的数据
    /// </summary>
    public void ClearPool()
    {
    poolDic.Clear();
    poolObj = null;
    }
    }

对象上限测试

设置从缓存池获取的对象上限分别为3和4,其他内容不变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using UnityEngine;

public class Main : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
PoolManager.Instance.GetObj("Test/Cube", 3);
}

if (Input.GetMouseButtonDown(1))
{
PoolManager.Instance.GetObj("Test/Sphere", 4);
}
}
}

按照一定频率反复按下鼠标左键和右键,场景上创建的两种对象数量会限制在3和4

image

将创建出来的对象会执行的逻辑改为向前无限移动而不是延迟删除

1
2
3
4
5
6
7
public class TestMove : MonoBehaviour
{
void Update()
{
this.transform.Translate(10 * Time.deltaTime * Vector3.forward);
}
}

由于从缓存池拿出来的对象的状态(位置,旋转,缩放等等)是不确定的,因此我们必须在拿出来的那一刻就对这个对象进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using UnityEngine;

public class Main : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
PoolManager.Instance.GetObj("Test/Cube", 3);
obj.transform.position = Vector3.zero;
}

if (Input.GetMouseButtonDown(1))
{
PoolManager.Instance.GetObj("Test/Sphere", 4);
obj.transform.position = new Vector3(1, 0, 0);
}
}
}

按照一定频率反复按下鼠标左键和右键,创建上向前移动的对象数量会恒定在在3和4,当达到上限时在从对象池取出,对象会自动回到起点

image

上限消息由参数传入改为对象属性

现在我们确定最大容量是通过在获取时传入参数maxNum​,若传入参数出错可能会导致“超上限”
能否优化下,以其它思路去制作,让我们可以更加方便的处理上限逻辑

  1. 让使用者不用每次设置上限值
  2. 初始化抽屉时,第一次就直接定好上限为多少,之后直接在内部判断即可

让缓存池对象挂载一个用于配置上限值的脚本,只需要在制作预设体时,将脚本挂好,设置好上限即可

1
2
3
4
5
6
7
8
9
using UnityEngine;

/// <summary>
/// 该脚本主要用于挂载到需要使用缓存池功能的预设体对象上
/// </summary>
public class PoolObj : MonoBehaviour
{
public int maxNum;
}

image

然后在PoolData​内声明一个上限值,在构造函数内通过读取预设体上的脚本设置的上限值属性
在声明一个NeedCreate​属性,当使用中的对象数量小于最大容量时,代表可以创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class PoolData
{
//抽屉根对象,用来就行布局管理的对象
private GameObject poolObj;
//用来存储抽屉中的对象,记录没有使用的对象
private Stack<GameObject> dataStack = new Stack<GameObject>();
//抽屉上限,场景上同时存在的对象的上限个数
private int maxNum;
//记录使用中的对象的列表
private List<GameObject> usedList = new List<GameObject>();
//获取容器中是否有对象
public int Count => dataStack.Count;

public int UsedCount => usedList.Count;

/// <summary>
/// 与使用中的对象数量与最大容量进行比较,小于返回true,需要实例化
/// </summary>
public bool NeedCreate => usedList.Count < maxNum;

public PoolData(GameObject poolRootObj, string poolName, GameObject usedObj)
{
//开启功能时,才会动态创建父子关系
if (PoolManager.isOpenLayout)
{
//创建抽屉父对象,和柜子父对象建立父子关系
poolObj = new GameObject(poolName);
poolObj.transform.SetParent(poolRootObj.transform);
}
//创建抽屉时,外部肯定是会动态创建一个对象的,我们应该将其记录到使用中的对象容器中
PushUsedList(usedObj);
PoolObj obj = usedObj.GetComponent<PoolObj>();
if (obj == null)
{
Debug.LogError("请为缓存池功能的预设体对象挂载PoolObj脚本,用于设置数量上限");
return;
}
//记录上限数量值
this.maxNum = obj.maxNum;
}

// 从抽屉中弹出数据对象
public GameObject Pop(); //..

// 将数据压入到抽屉内
public void Push(GameObject obj); //..

// 将对象添加到使用中的列表
public void PushUsedList(GameObject obj); //..
}

同时修改PoolManager​的GetObj​方法,去除maxNum​参数,使用NeedCreate​属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public GameObject GetObj(string name)
{
//如果根物体为空且自动排布开启,就创建
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");

GameObject obj;
//无抽屉,或者抽屉无对象且使用中的对象也未超上限,需要新实例化一个对象
if (!poolDic.ContainsKey(name) ||
(poolDic[name].Count == 0 && poolDic[name].NeedCreate))
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
obj.name = name;
//无抽屉时,创建一个抽屉并将新对象传入到构造函数内
if (!poolDic.ContainsKey(name))
poolDic.Add(name, new PoolData(poolObj, name, obj));
//有抽屉时,将新创建的对象传入到使用中的列表内
else
poolDic[name].PushUsedList(obj);
}
//当抽屉有对象或者使用中的对象超上限了,就直接从抽屉内取出来使用
else // if (poolDic[name].Count > 0 || poolDic[name].UsedCount >= maxNum)
{
obj = poolDic[name].Pop();
}
return obj;
}