Unity学习笔记--如何优雅简便地利用对象池生成游戏对象(进阶版)LRU + 对象池

前言

之前写过一篇关于对象池的文章,现在来看写的并不是很好,所以来考虑优化下。

现在来看一年前写的代码,越看越不能入目hhh

Unity学习笔记--如何优雅简便地利用对象池生成游戏对象

前置知识

Unity学习笔记--使用 C# 开发一个 LRU

代码实现

PoolManager.cs

csharp 复制代码
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

csharp 复制代码
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;
        }
    }
}

使用

创建 Factory

csharp 复制代码
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();
    }
}

创建 object

csharp 复制代码
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

BulletManager.cs

csharp 复制代码
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

csharp 复制代码
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

csharp 复制代码
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

相关推荐
Eli_Optics4 分钟前
《通信原理》学习笔记——第三章
笔记·学习
屁股割了还要学19 分钟前
【C语言进阶】题目练习(2)
c语言·开发语言·c++·学习·算法·青少年编程
Sui_Network30 分钟前
Sui 在非洲增长最快的科技市场开设 SuiHub Lagos 以推动创新
大数据·人工智能·科技·游戏·web3·区块链
麟城Lincoln1 小时前
【Linux-云原生-笔记】LVS(Linux virual server)相关
linux·笔记·云原生·lvs·nat·dr·ipvsadm
枯萎穿心攻击1 小时前
响应式编程入门教程第五节:Unity 生命周期与资源管理中的响应式编程
开发语言·unity·架构·c#·游戏引擎
艾莉丝努力练剑2 小时前
【数据结构与算法】数据结构初阶:详解顺序表和链表(五)——双向链表
c语言·开发语言·数据结构·学习·算法
饕餮争锋3 小时前
设计模式笔记_结构型_装饰器模式
笔记·设计模式·装饰器模式
斯内科3 小时前
C#将【程序集引用-依赖关系】展示到NetronLight图表中
c#·流程图·graph
一只爱做笔记的码农4 小时前
【C#】Vscode中C#工程如何引用自编写的dll
开发语言·vscode·c#
楼田莉子4 小时前
Linux学习之认识Linux的基本指令
linux·运维·服务器·开发语言·学习