HartoukChartEditor/Assets/Script/Core/BasePool.cs

160 lines
5.8 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 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()既可以是我再子类对象池中覆写的函数,也可以是子类自带的初始化函数
当然采用回调函数的方法未免还是有点大题小做,可以通过仿照生命周期函数的方式,完成基类的定义,
并且,基类不进行实例化,它只可以被继承至子类,以保证安全性,我们必须防止所有的实例共用一个栈的情况发生
理论上是可行的
*/