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

前言

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

现在来看一年前写的代码,越看越不能入目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;
        }
    }
}

使用

创建 Factory

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

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

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

你可能感兴趣的:(Unity学习笔记,unity,学习,笔记,c#,游戏)