BITKit/Src/Unity/Scripts/Pool/UnityPoolService.cs

164 lines
5.5 KiB
C#

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using UnityEngine;
using Object = UnityEngine.Object;
namespace BITKit.Pool
{
public class UnityPoolService:IPoolService,IDisposable
{
private readonly IServiceProvider _serviceProvider;
public UnityPoolService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
private static readonly ConcurrentDictionary<string,List<object>> Pool = new();
private static readonly ConcurrentDictionary<string,List<object>> UsingPool = new();
private static readonly ConcurrentDictionary<string, Queue<object>> ReadyPool = new();
public void Dispose()
{
var hashset = new HashSet<GameObject>();
foreach (var (_,list) in Pool)
{
foreach (var obj in list)
{
if (obj is not Component component) continue;
if (!hashset.Add(component.gameObject)) continue;
if (component.gameObject)
Object.Destroy(component.gameObject);
}
}
Pool.Clear();
UsingPool.Clear();
ReadyPool.Clear();
}
public int DefaultCapacity { get; set; } = 8;
public async UniTask<T> Spawn<T>(string path,object prefab) where T : class
{
if (Pool.ContainsKey(path))
{
var readyQueue = ReadyPool[path];
var usingList = UsingPool[path];
if (readyQueue.TryDequeue(out var obj))
{
var value = obj as T;
usingList.Add(value);
return obj as T;
}
if (usingList.Count>=DefaultCapacity)
{
obj = usingList[0];
usingList.RemoveAt(0);
usingList.Add(obj);
#if UNITY_5_3_OR_NEWER
if (obj is GameObject gameObject)
{
gameObject.SetActive(false);
gameObject.SetActive(true);
}
#endif
return obj as T;
}
}
var list = Pool.GetOrCreate(path);
UsingPool.GetOrCreate(path);
ReadyPool.GetOrCreate(path);
#if UNITY_5_3_OR_NEWER
if (typeof(Object).IsAssignableFrom(typeof(T)))
{
var asset =prefab as T ?? await ModService.LoadAsset<T>(path);
if (asset is Object o)
{
var instance = Object.Instantiate(o);
list.Add(instance);
UsingPool.GetOrCreate(path).Add(instance);
ReadyPool.GetOrCreate(path);
if (instance is GameObject gameObject)
{
gameObject.GetCancellationTokenOnDestroy().Register(DisposeGo);
void DisposeGo()
{
Pool.GetOrCreate(path).TryRemove(gameObject);
UsingPool.GetOrCreate(path).TryRemove(gameObject);
var queue = ReadyPool.GetOrCreate(path);
var removeObjectList = new List<object>(queue);
removeObjectList.TryRemove(gameObject);
queue.Clear();
foreach (var x in removeObjectList)
{
queue.Enqueue(x);
}
}
}
return instance as T;
}
}
#endif
//检查T的构造函数,如果有需要参数的构造函数,就从ServiceProvider中获取,如果没有,则直接通过System.Activator创建
if (typeof(T).GetConstructors().Any(constructorInfo => constructorInfo.GetParameters().Length is 0))
{
var value = Activator.CreateInstance<T>();
list.Add(value);
UsingPool.GetOrCreate(path).Add(value);
ReadyPool.GetOrCreate(path);
return value;
}
{
var value = _serviceProvider.GetRequiredService<T>();
list.Add(value);
UsingPool.GetOrCreate(path).Add(value);
ReadyPool.GetOrCreate(path);
return value;
}
}
public void Despawn<T>(T obj, string path) where T : class
{
if (UsingPool.TryGetValue(path, out var value))
{
value.Remove(obj);
ReadyPool[path].Enqueue(obj);
#if UNITY_5_3_OR_NEWER
if (obj is GameObject gameObject)
{
gameObject.SetActive(false);
}
#endif
return;
}
if (Pool.ContainsKey(path)) return;
Pool.GetOrCreate(path).Add(obj);
ReadyPool.GetOrCreate(path).Enqueue(obj);
}
public UniTask InitializeAsync()
{
return UniTask.CompletedTask;
}
}
}