之前写过一篇关于对象池的文章,现在来看写的并不是很好,所以来考虑优化下。
现在来看一年前写的代码,越看越不能入目hhh
Unity学习笔记–如何优雅简便地利用对象池生成游戏对象
Unity学习笔记–使用 C# 开发一个 LRU
PoolManager.cs
using System;
using System.Collections.Generic;
using Factory;
namespace ToolManager
{
public class PoolManager
{
private Dictionary<string, LinkedListNode<Tuple<string, Pool>>> lru_dict; // Key : pool_name == obj_name
private LinkedList<Tuple<string, Pool>> cache;
private int capacity;
public PoolManager(int capacity_in = 64)
{
capacity = capacity_in;
cache = new LinkedList<Tuple<string, Pool>>();
lru_dict = new Dictionary<string, LinkedListNode<Tuple<string, Pool>>>();
}
public bool HasPool(string path)
{
return lru_dict.ContainsKey(path);
}
public bool AddPool(BaseFactory factory, int init_count = 0)
{
string pool_name = factory.GetObjPath();
if (HasPool(pool_name))
{
return false;
}
Pool pool = new Pool(this, pool_name, factory, init_count);
LinkedListNode<Tuple<string, Pool>> node = new LinkedListNode<Tuple<string, Pool>>(Tuple.Create(pool_name, pool));
LRUAdd(node);
return true;
}
public bool DelPool(string name)
{
if (!HasPool(name))
{
return false;
}
var node = lru_dict[name];
GetPool(node).ReleasePool();
LRURemove(node);
return true;
}
public object PopOne(string name)
{
object res = null;
if (HasPool(name))
{
var node = lru_dict[name];
LRUChange(node);
Pool pool = GetPool(node);
res = pool.PopOne();
LRURemove(node);
}
return res;
}
public object[] Pop(string name, int count)
{
object[] res = null;
if (HasPool(name))
{
var node = lru_dict[name];
LRUChange(node);
Pool pool = GetPool(node);
res = pool.Pop(count);
LRURemove(node);
}
return res;
}
public void PushOne(string name, object obj)
{
if (HasPool(name))
{
var node = lru_dict[name];
LRUChange(node);
GetPool(node).PushOne(obj);
RefreshLRU();
}
}
public void Push(string name, object[] objs)
{
if (HasPool(name))
{
var node = lru_dict[name];
LRUChange(node);
GetPool(node).Push(objs);
RefreshLRU();
}
}
private Pool GetPool(LinkedListNode<Tuple<string, Pool>> node)
{
return node.Value.Item2;
}
// ------------------------- LRU Function -------------------------
private void LRUChange(LinkedListNode<Tuple<string, Pool>> node)
{
cache.Remove(node);
cache.AddLast(node);
}
private void LRUAdd(LinkedListNode<Tuple<string, Pool>> node)
{
lru_dict.Add(node.Value.Item1, node);
cache.AddLast(node);
}
private void LRURemove(LinkedListNode<Tuple<string, Pool>> node)
{
cache.Remove(node);
lru_dict.Remove(node.Value.Item1);
}
private void RefreshLRU()
{
int lru_count = LRUCacheCount;
while (lru_count > capacity)
{
Pool pool = GetPool(cache.First);
int n_objects_to_remove = Math.Min(pool.PoolCount, lru_count - capacity);
for (int i = 0; i < n_objects_to_remove; i++)
{
pool.ReleaseOne();
}
if(pool.PoolCount == 0)
{
DelPool(pool.pool_name);
}
lru_count = LRUCacheCount;
}
}
private int LRUCacheCount
{
get
{
int count = 0;
foreach (var node in lru_dict.Values)
{
count += node.Value.Item2.PoolCount;
}
return count;
}
}
private class Pool
{
private PoolManager pool_mgr;
private BaseFactory factory;
private Queue<object> queue;
public string pool_name;
public Pool(PoolManager pool_mgr_in, string pool_name_in, BaseFactory factory_in, int init_count_in)
{
pool_mgr = pool_mgr_in;
pool_name = pool_name_in;
factory = factory_in;
queue = new Queue<object>();
InitPool(init_count_in);
}
private void InitPool(int init_count)
{
for (int i = 0; i < init_count; i++)
{
queue.Enqueue(factory.CreateObject());
}
}
public void ReleasePool()
{
foreach (var obj in queue)
{
factory.DestroyObject(obj);
}
queue.Clear();
factory.ReleaseFactory();
}
public object PopOne()
{
if (queue.Count > 0)
{
object obj = queue.Dequeue();
factory.ResetObject(obj);
return obj;
}
return factory.CreateObject();
}
public object[] Pop(int count)
{
object[] objs = new object[count];
for (int i = 0; i < count; i++)
{
objs[i] = PopOne();
}
return objs;
}
public void PushOne(object obj)
{
factory.RecycleObject(obj);
queue.Enqueue(obj);
}
public void Push(object[] objs)
{
foreach (var obj in objs)
{
PushOne(obj);
}
}
public void ReleaseOne()
{
factory.DestroyObject(queue.Dequeue());
}
public int PoolCount { get => queue.Count; }
}
}
}
BaseFactory.cs
namespace Factory
{
public abstract class BaseFactory
{
protected string obj_path;
public BaseFactory(string obj_path_in)
{
obj_path = obj_path_in;
}
public abstract object CreateObject();
public abstract void ResetObject(object obj);
public abstract void RecycleObject(object obj);
public abstract void DestroyObject(object obj);
public abstract void ReleaseFactory();
public virtual string GetObjPath()
{
return obj_path;
}
}
}
using Factory;
public class BulletFactory : BaseFactory
{
public BulletFactory(string obj_path_in) : base(obj_path_in)
{
}
public override object CreateObject()
{
Bullet bullet = new Bullet(obj_path);
bullet.ReuseInit();
return bullet;
}
public override void DestroyObject(object obj)
{
((Bullet)obj).Destroy();
}
public override void RecycleObject(object obj)
{
((Bullet)obj).Recycle();
}
public override void ReleaseFactory()
{
}
public override void ResetObject(object obj)
{
((Bullet)obj).ReuseInit();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet
{
private GameObject go;
private string path;
private static GameObject prefab;
public Bullet(string path_in)
{
path = path_in;
if (prefab == null)
{
prefab = (GameObject) Resources.Load(path);
}
}
public void ReuseInit()
{
if (go)
{
go.SetActive(true);
}
else
{
go = GameObject.Instantiate(prefab);
}
go.GetComponent<RecycleMe>().DelayCall(1, Func);
}
public void Destroy()
{
GameObject.Destroy(go);
}
public void Recycle()
{
go.SetActive(false);
}
private void Func()
{
BulletManager.Ins.PushOne(path, this);
}
}
BulletManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ToolManager;
public class BulletManager : MonoBehaviour
{
private PoolManager pool_mgr;
private static BulletManager _ins;
public static BulletManager Ins
{
get => _ins;
}
private void Awake()
{
Init();
}
private void Init()
{
_ins = this;
pool_mgr = new PoolManager();
}
public void PushOne(string path, Bullet obj)
{
if (!pool_mgr.HasPool(path))
{
pool_mgr.AddPool(new BulletFactory(path));
}
pool_mgr.PushOne(path, obj);
}
public Bullet PopOne(string path)
{
if (!pool_mgr.HasPool(path))
{
pool_mgr.AddPool(new BulletFactory(path));
}
Bullet bullet = (Bullet)pool_mgr.PopOne(path);
return bullet;
}
}
为了验证,我写了一个 ShootManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShootManager : MonoBehaviour
{
private List<Bullet> bullet_list;
private string path = "Bullet";
private static ShootManager _ins;
public static ShootManager Ins
{
get => _ins;
}
private void Awake()
{
Init();
}
private void Init()
{
_ins = this;
bullet_list = new List<Bullet>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
bullet_list.Add(BulletManager.Ins.PopOne(path));
}
}
}
创建之后过 1s 之后回收自己。
RecycleMe.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RecycleMe : MonoBehaviour
{
public void DelayCall(float delay, Action func)
{
StartCoroutine(DestroyAfterDelayCoroutine(delay, func));
}
IEnumerator DestroyAfterDelayCoroutine(float delay, Action func)
{
yield return new WaitForSeconds(delay);
func.Invoke();
}
}
LRUPool