CutePet/Assets/Scripts/Framework/Pool/PoolMgr.cs
2024-10-25 11:10:04 +08:00

350 lines
9.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 抽屉(池子中的数据)对象
/// </summary>
public class PoolData
{
//用来存储抽屉中的对象 记录的是没有使用的对象
private Stack<GameObject> dataStack = new Stack<GameObject>();
//用来记录使用中的对象的
private List<GameObject> usedList = new List<GameObject>();
//抽屉上限 场景上同时存在的对象的上限个数
private int maxNum;
//抽屉根对象 用来进行布局管理的对象
private GameObject rootObj;
//获取容器中是否有对象
public int Count => dataStack.Count;
public int UsedCount => usedList.Count;
/// <summary>
/// 进行使用中对象数量和最大容量进行比较 小于返回true 需要实例化
/// </summary>
public bool NeedCreate => usedList.Count < maxNum;
/// <summary>
/// 初始化构造函数
/// </summary>
/// <param name="root">柜子(缓存池)父对象</param>
/// <param name="name">抽屉父对象的名字</param>
public PoolData(GameObject root, string name, GameObject usedObj)
{
//开启功能时 才会动态创建 建立父子关系
if(PoolMgr.isOpenLayout)
{
//创建抽屉父对象
rootObj = new GameObject(name);
//和柜子父对象建立父子关系
rootObj.transform.SetParent(root.transform);
}
//创建抽屉时 外部肯定是会动态创建一个对象的
//我们应该将其记录到 使用中的对象容器中
PushUsedList(usedObj);
PoolObj poolObj = usedObj.GetComponent<PoolObj>();
if (poolObj == null)
{
Debug.LogError("请为使用缓存池功能的预设体对象挂载PoolObj脚本 用于设置数量上限");
return;
}
//记录上限数量值
maxNum = poolObj.maxNum;
}
/// <summary>
/// 从抽屉中弹出数据对象
/// </summary>
/// <returns>想要的对象数据</returns>
public GameObject Pop()
{
//取出对象
GameObject obj;
if (Count > 0)
{
//从没有的容器当中取出使用
obj = dataStack.Pop();
//现在要使用了 应该要用使用中的容器记录它
usedList.Add(obj);
}
else
{
//取0索引的对象 代表的就是使用时间最长的对象
obj = usedList[0];
//并且把它从使用着的对象中移除
usedList.RemoveAt(0);
//由于它还要拿出去用,所以我们应该把它又记录到 使用中的容器中去
//并且添加到尾部 表示 比较新的开始
usedList.Add(obj);
}
//激活对象
obj.SetActive(true);
//断开父子关系
if (PoolMgr.isOpenLayout)
obj.transform.SetParent(null);
return obj;
}
/// <summary>
/// 将物体放入到抽屉对象中
/// </summary>
/// <param name="obj"></param>
public void Push(GameObject obj)
{
//失活放入抽屉的对象
obj.SetActive(false);
//放入对应抽屉的根物体中 建立父子关系
if (PoolMgr.isOpenLayout)
obj.transform.SetParent(rootObj.transform);
//通过栈记录对应的对象数据
dataStack.Push(obj);
//这个对象已经不再使用了 应该把它从记录容器中移除
usedList.Remove(obj);
}
/// <summary>
/// 将对象压入到使用中的容器中记录
/// </summary>
/// <param name="obj"></param>
public void PushUsedList(GameObject obj)
{
usedList.Add(obj);
}
}
/// <summary>
/// 方便在字典当中用里式替换原则 存储子类对象
/// </summary>
public abstract class PoolObjectBase { }
/// <summary>
/// 用于存储 数据结构类 和 逻辑类 不继承mono的容器类
/// </summary>
/// <typeparam name="T"></typeparam>
public class PoolObject<T> : PoolObjectBase where T:class
{
public Queue<T> poolObjs = new Queue<T>();
}
/// <summary>
/// 想要被复用的 数据结构类、逻辑类 都必须要继承该接口
/// </summary>
public interface IPoolObject
{
/// <summary>
/// 重置数据的方法
/// </summary>
void ResetInfo();
}
/// <summary>
/// 缓存池(对象池)模块 管理器
/// </summary>
public class PoolMgr : BaseManager<PoolMgr>
{
//柜子容器当中有抽屉的体现
//值 其实代表的就是一个 抽屉对象
private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
/// <summary>
/// 用于存储数据结构类、逻辑类对象的 池子的字典容器
/// </summary>
private Dictionary<string, PoolObjectBase> poolObjectDic = new Dictionary<string, PoolObjectBase>();
//池子根对象
private GameObject poolObj;
//是否开启布局功能
public static bool isOpenLayout = true;
private PoolMgr() {
//如果根物体为空 就创建
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");
}
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name">抽屉容器的名字</param>
/// <returns>从缓存池中取出的对象</returns>
public GameObject GetObj(string name)
{
//如果根物体为空 就创建
if (poolObj == null && isOpenLayout)
poolObj = new GameObject("Pool");
GameObject obj;
#region
if(!poolDic.ContainsKey(name) ||
(poolDic[name].Count == 0 && poolDic[name].NeedCreate))
{
//动态创建对象
//没有的时候 通过资源加载 去实例化出一个GameObject
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
//避免实例化出来的对象 默认会在名字后面加一个(Clone)
//我们重命名过后 方便往里面放
obj.name = name;
//创建抽屉
if(!poolDic.ContainsKey(name))
poolDic.Add(name, new PoolData(poolObj, name, obj));
else//实例化出来的对象 需要记录到使用中的对象容器中
poolDic[name].PushUsedList(obj);
}
//当抽屉中有对象 或者 使用中的对象超上限了 直接去取出来用
else
{
obj = poolDic[name].Pop();
}
#endregion
#region
////有抽屉 并且 抽屉里 有对象 才去直接拿
//if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
//{
// //弹出栈中的对象 直接返回给外部使用
// obj = poolDic[name].Pop();
//}
////否则,就应该去创造
//else
//{
// //没有的时候 通过资源加载 去实例化出一个GameObject
// obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
// //避免实例化出来的对象 默认会在名字后面加一个(Clone)
// //我们重命名过后 方便往里面放
// obj.name = name;
//}
#endregion
return obj;
}
/// <summary>
/// 获取自定义的数据结构类和逻辑类对象 不继承Mono的
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <returns></returns>
public T GetObj<T>(string nameSpace = "") where T:class,IPoolObject,new()
{
//池子的名字 是根据类的类型来决定的 就是它的类名
string poolName = nameSpace + "_" + typeof(T).Name;
//有池子
if(poolObjectDic.ContainsKey(poolName))
{
PoolObject<T> pool = poolObjectDic[poolName] as PoolObject<T>;
//池子当中是否有可以复用的内容
if(pool.poolObjs.Count > 0)
{
//从队列中取出对象 进行复用
T obj = pool.poolObjs.Dequeue() as T;
return obj;
}
//池子当中是空的
else
{
//必须保证存在无参构造函数
T obj = new T();
return obj;
}
}
else//没有池子
{
T obj = new T();
return obj;
}
}
/// <summary>
/// 往缓存池中放入对象
/// </summary>
/// <param name="name">抽屉(对象)的名字</param>
/// <param name="obj">希望放入的对象</param>
public void PushObj(GameObject obj)
{
#region
////总之,目的就是要把对象隐藏起来
////并不是直接移除对象 而是将对象失活 一会儿再用 用的时候再激活它
////除了这种方式,还可以把对象放倒屏幕外看不见的地方
//obj.SetActive(false);
////把失活的对象(要放入抽屉中的对象) 父对象先设置为 柜子(缓存池)根对象
//obj.transform.SetParent(poolObj.transform);
#endregion
//没有抽屉 创建抽屉
//if (!poolDic.ContainsKey(obj.name))
// poolDic.Add(obj.name, new PoolData(poolObj, obj.name));
//往抽屉当中放对象
poolDic[obj.name].Push(obj);
////如果存在对应的抽屉容器 直接放
//if(poolDic.ContainsKey(name))
//{
// //往栈(抽屉)中放入对象
// poolDic[name].Push(obj);
//}
////否则 需要先创建抽屉 再放
//else
//{
// //先创建抽屉
// poolDic.Add(name, new Stack<GameObject>());
// //再往抽屉里面放
// poolDic[name].Push(obj);
//}
}
/// <summary>
/// 将自定义数据结构类和逻辑类 放入池子中
/// </summary>
/// <typeparam name="T">对应类型</typeparam>
public void PushObj<T>(T obj, string nameSpace = "") where T:class,IPoolObject
{
//如果想要压入null对象 是不被允许的
if (obj == null)
return;
//池子的名字 是根据类的类型来决定的 就是它的类名
string poolName = nameSpace + "_" + typeof(T).Name;
//有池子
PoolObject<T> pool;
if (poolObjectDic.ContainsKey(poolName))
//取出池子 压入对象
pool = poolObjectDic[poolName] as PoolObject<T>;
else//没有池子
{
pool = new PoolObject<T>();
poolObjectDic.Add(poolName, pool);
}
//在放入池子中之前 先重置对象的数据
obj.ResetInfo();
pool.poolObjs.Enqueue(obj);
}
/// <summary>
/// 用于清除整个柜子当中的数据
/// 使用场景 主要是 切场景时
/// </summary>
public void ClearPool()
{
poolDic.Clear();
poolObj = null;
poolObjectDic.Clear();
}
}