Unity study notes--how to use the object pool to generate game objects elegantly and easily (advanced version) LRU + object pool

foreword

I wrote an article about the object pool before, but now it’s not very good, so let’s consider optimizing it.

Now let’s look at the code written a year ago, the more you look at it, the more you can’t see it hhh

Unity study notes – how to use the object pool to generate game objects elegantly and easily

Pre-knowledge

Unity study notes – using C# to develop an LRU

Code

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;
        }
    }
}

use

createFactory

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();
    }
}

create 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);
    }
}

Create a 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;
    }
}

verify

For verification, I wrote a 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));
        }
    }
}

Recycle itself after 1s after creation.
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();
    }

}

Effect

LRUPool

Guess you like

Origin blog.csdn.net/qq_52855744/article/details/132205487