160 lines
5.8 KiB
C#
160 lines
5.8 KiB
C#
using UnityEngine;
|
||
using System.Collections.Generic;
|
||
using System;
|
||
using System.Collections.ObjectModel;
|
||
using System.Linq;
|
||
|
||
/// <summary>
|
||
/// 泛型对象池的父类,支持活跃对象的查找
|
||
/// </summary>
|
||
/// <typeparam name="BaseClass">基类对象池</typeparam>
|
||
public abstract class BasePool<BaseClass> : MonoBehaviour where BaseClass : MonoBehaviour
|
||
{
|
||
/// <summary>
|
||
/// 对象池本体
|
||
/// </summary>
|
||
protected Dictionary<Type, Stack<BaseClass>> Pools = new Dictionary<Type, Stack<BaseClass>>();
|
||
/// <summary>
|
||
/// 活跃对象记录表
|
||
/// </summary>
|
||
protected Dictionary<Type, List<BaseClass>> ActiveObjectList = new Dictionary<Type, List<BaseClass>>();
|
||
|
||
/// <summary>
|
||
/// 依据一个原型,返回一个空的可用对象
|
||
/// </summary>
|
||
public DerivedClass Get<DerivedClass>(DerivedClass prefab) where DerivedClass : BaseClass
|
||
{
|
||
var type = typeof(DerivedClass);
|
||
if (!Pools.ContainsKey(type))
|
||
{
|
||
Pools[type] = new Stack<BaseClass>();
|
||
ActiveObjectList[type] = new List<BaseClass>();
|
||
}
|
||
DerivedClass obj;
|
||
//对象池逻辑
|
||
if (Pools[type].Count > 0)
|
||
{
|
||
obj = (DerivedClass)Pools[type].Pop();
|
||
ActiveObjectList[type].Add(obj);
|
||
}
|
||
else
|
||
{
|
||
//如果对象池没有对象,则创建一个对象
|
||
obj = Instantiate(prefab);
|
||
ActiveObjectList[type].Add(obj);
|
||
OnCreate(obj);
|
||
}
|
||
OnGet(obj);
|
||
obj.gameObject.SetActive(true);
|
||
obj.enabled = true;
|
||
|
||
return obj;
|
||
}
|
||
/// <summary>
|
||
/// 回收当前对象
|
||
/// </summary>
|
||
public void Release(BaseClass Object)
|
||
{
|
||
var type = Object.GetType();
|
||
//如果字典中没有该类的对象池,则创建一个该类型的对象池
|
||
if (!Pools.ContainsKey(type)) Pools[type] = new Stack<BaseClass>();
|
||
|
||
OnRelease(Object);
|
||
Object.gameObject.SetActive(false);
|
||
|
||
Pools[type].Push(Object);
|
||
ActiveObjectList[type].Remove(Object);
|
||
}
|
||
/// <summary>
|
||
/// 该对象是否有被实例化
|
||
/// </summary>
|
||
/// <param name="Object"></param>
|
||
/// <returns></returns>
|
||
public bool ContainsObject(BaseClass Object)
|
||
{
|
||
var type = Object.GetType();
|
||
//如果没有该类型的对象池,则返回假
|
||
if (!Pools.ContainsKey(type)) return false;
|
||
return ActiveObjectList[type].Contains(Object);
|
||
}
|
||
/// <summary>
|
||
/// 清空对象池
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <paramref name="该操作会清空所有的对象"/>
|
||
/// </remarks>
|
||
public void ClearPool()
|
||
{
|
||
foreach (var stack in Pools.Values)
|
||
{
|
||
while (stack.Count > 0)
|
||
{
|
||
Destroy(stack.Pop().gameObject);
|
||
}
|
||
}
|
||
Pools.Clear();
|
||
}
|
||
/// <summary>
|
||
/// 返回一个当前类型的只读列表
|
||
/// </summary>
|
||
public ReadOnlyCollection<DerivedClass> GetActiveObjectList<DerivedClass>() where DerivedClass : BaseClass
|
||
{
|
||
var type=typeof(DerivedClass);
|
||
if (!Pools.ContainsKey(type))
|
||
{
|
||
ActiveObjectList[type] = new List<BaseClass>();
|
||
}
|
||
//what the fuck ? this code is SHIT!
|
||
return ActiveObjectList[type].Cast<DerivedClass>().ToList().AsReadOnly();
|
||
}
|
||
/// <summary>
|
||
/// 对指定类型的所有活跃对象执行指定操作
|
||
/// </summary>
|
||
/// <typeparam name="DerivedClass">要操作的派生类型</typeparam>
|
||
/// <param name="action">要对每个对象执行的操作</param>
|
||
/// <param name="includeInactive">是否包含非活跃对象(已回收但未销毁的对象)</param>
|
||
public void ForEach<DerivedClass>(Action<DerivedClass> action) where DerivedClass : BaseClass
|
||
{
|
||
var type = typeof(DerivedClass);
|
||
|
||
// 检查是否有该类型的活跃对象列表
|
||
if (!ActiveObjectList.TryGetValue(type, out var list) || list == null) return;
|
||
|
||
for (int i = list.Count - 1; i >= 0; i--)
|
||
{
|
||
var item = (DerivedClass)list[i];
|
||
action?.Invoke(item);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 当对象首次创建时调用
|
||
/// </summary>
|
||
protected virtual void OnCreate(BaseClass obj) { }
|
||
|
||
/// <summary>
|
||
/// 当对象从池中取出时调用
|
||
/// </summary>
|
||
protected virtual void OnGet(BaseClass obj) { }
|
||
|
||
/// <summary>
|
||
/// 当对象回收到池中时调用
|
||
/// </summary>
|
||
protected virtual void OnRelease(BaseClass obj) { }
|
||
}
|
||
/*
|
||
首先必须分清楚我们需要的是哪种对象池。
|
||
在一个类里面完成所有不同对象池功能的定义是绝对不现实的,这是显而易见的。
|
||
但是我们又想实现一定意义上的对象池复用,所以我们需要的其实是一个可重复实例化的对象池。
|
||
很容易的得到,这个对象池应该继承与MonoBehaviour,因为其实所有的脚本类其实都继承与这个类。
|
||
所以依据里氏转换原则,MonoBehaviour可以被转换为任意我们已有的脚本。
|
||
并且可以使用unity相关的API,如SetActive(false)等。
|
||
|
||
我们的对象仅仅只能在必要时 生成/初始化 但是不同类型的对象完全不一样,为了实现基对象池与限定类型的解耦合,
|
||
我们可以采用回调函数的方式,即定义一个Action()类型的的回调函数,并且在每次对象池调用对象时,即刻执行这个Action()
|
||
这个Action()既可以是我再子类对象池中覆写的函数,也可以是子类自带的初始化函数
|
||
|
||
当然采用回调函数的方法未免还是有点大题小做,可以通过仿照生命周期函数的方式,完成基类的定义,
|
||
并且,基类不进行实例化,它只可以被继承至子类,以保证安全性,我们必须防止所有的实例共用一个栈的情况发生
|
||
|
||
理论上是可行的
|
||
*/ |