Josip Å tajdohar

ObjectPoolerBase.cs PoolClasses.cs ObjectPooler.cs EnemyPooler.cs
The implementation of the pooler for enemy unit's. In addition to it becoming a singelton, it also overrides the the abstract class so that it can: properly set the stats of the created unit, reset unit's hp to full when spawning, keep track of it's spawned unit's count.

                using System.Collections;
                using System.Collections.Generic;
                using UnityEngine;
                
                namespace ObjectPooling
                {
                    /// <summary>
                    /// A class that is used to pool <see cref="Enemy"/>'s.
                    /// </summary>
                    public class EnemyPooler : ObjectPoolerBase<EnemyType>
                    {
                        /// <summary>
                        /// A list of templates <see cref="EnemyPoolItem"/> by which we populate the pool.
                        /// </summary>
                        [SerializeField]
                        private List<EnemyPoolItem> enemyPoolItems;
                
                        /// <summary>
                        /// The shared instanced of this object.
                        /// </summary>
                        public static EnemyPooler SharedInstance;
                
                        /// <summary>
                        /// Dictionary storing information on the number of enemy's that are active.
                        /// </summary>
                        private Dictionary<EnemyType, int> pooledEnemyCount = new Dictionary<EnemyType, int>();
                
                        /// <summary>
                        /// Storing the instance.
                        /// </summary>
                        private void Awake()
                        {
                            if (SharedInstance != null)
                            {
                                Debug.LogError("Multiple instances of EnemyPooler exist");
                            }
                
                            SharedInstance = this;
                
                            foreach (EnemyPoolItem enemyPoolItem in enemyPoolItems)
                            {
                                itemsToPool.Add(enemyPoolItem);
                            }
                        }
                
                        /// <summary>
                        /// Do we have any <see cref="Enemy"/> outside of the pool.
                        /// </summary>
                        /// <returns> True if we do, otherwise false. </returns>
                        public bool AnyEnemysSpawned()
                        {
                            bool value = false;
                
                            foreach (int count in pooledEnemyCount.Values)
                            {
                                if (count > 0) value = true;
                            }
                
                            return value;
                        }
                
                        /// <summary>
                        /// In addition to <see cref="ObjectPoolerBase{T}.PopulateDictionarys(ObjectPoolBase{T}, GameObject, Queue{GameObject})"/> we populate the
                        /// <see cref="pooledEnemyCount"/> dictionary.
                        /// </summary>
                        /// <param name="item"> The item for <see cref="ObjectPoolerBase{T}.poolItemDictionary"/>.</param>
                        /// <param name="poolParent"> The transform that is the parent of instantiated objects for <see cref="ObjectPoolerBase{T}.pooledObjectsParents"/>. </param>
                        /// <param name="pool"> The pool for <see cref="ObjectPoolerBase{T}.pooledObjectsQueue"/>. </param>
                        protected override void PopulateDictionarys(ObjectPoolBase<EnemyType> item, GameObject poolParent, Queue<GameObject> pool)
                        {
                            base.PopulateDictionarys(item, poolParent, pool);
                            EnemyType type = item.ReturnType();
                            pooledEnemyCount.Add(type, 0);
                        }
                
                        /// <summary>
                        /// In addition to <see cref="ObjectPoolerBase{T}.SpawnFromPool(T, Vector3, Quaternion, Transform)"/> we reset the hp of the <see cref="Enemy"/>
                        /// and increment the correct value in <see cref="pooledEnemyCount"/>.
                        /// </summary>
                        /// <param name="poolType"> Type of the object that is pooled. </param>
                        /// <param name="position"> The local position it gets spawned in. </param>
                        /// <param name="rotation"> The local rotation it gets spawned in. </param>
                        /// <param name="parent"> The transform that will be the child of the spawned item. </param>
                        /// <returns> Returns the spawned object or null if the pool is empty and cant be expanded. </returns>
                        public override GameObject SpawnFromPool(EnemyType poolType, Vector3 position, Quaternion rotation, Transform parent)
                        {
                            GameObject enemyGO = base.SpawnFromPool(poolType, position, rotation, parent);
                            enemyGO.GetComponent<Enemy>().ResetHP();
                            pooledEnemyCount[poolType]++;
                
                            return enemyGO;
                        }
                
                        /// <summary>
                        /// In addition to <see cref="ObjectPoolerBase{T}.ReturnToPool(T, GameObject)"/> we decrement the correct value in <see cref="pooledEnemyCount"/>.
                        /// </summary>
                        /// <param name="poolType"> Pool type of the object. </param>
                        /// <param name="gameObject"> The object that is returning. </param>
                        public override void ReturnToPool(EnemyType poolType, GameObject gameObject)
                        {
                            base.ReturnToPool(poolType, gameObject);
                            pooledEnemyCount[poolType]--;
                        }
                
                        /// <summary>
                        /// In addition to <see cref="ObjectPoolerBase{T}.InstantiateObjectToPool(Queue{GameObject}, ObjectPoolBase{T}, Transform)"/> we instantiate a new 
                        /// <see cref="ScriptableEnemy"/> for the instantiated <see cref="Enemy"/> and populate correctly.
                        /// </summary>
                        /// <param name="pool"> The pool the instantiated object will be added to. </param>
                        /// <param name="item"> The <see cref="ObjectPoolBase{T}"/> item that is used as a template for instantiation. </param>
                        /// <param name="parent"> The parent transform that will hold the instantiated object in the hierarchy. </param>
                        /// <returns> The instantiated game object. </returns>
                        protected override GameObject InstantiateObjectToPool(Queue<GameObject> pool, ObjectPoolBase<EnemyType> item, Transform parent)
                        {
                            GameObject obj = base.InstantiateObjectToPool(pool, item, parent);
                            EnemyPoolItem enemyPoolItem = item as EnemyPoolItem;
                
                            Enemy enemy = obj.GetComponent<Enemy>();
                            ScriptableEnemy scriptable = (ScriptableEnemy)Instantiate(enemyPoolItem.scriptableEnemy);
                            enemy.SetScriptableEnemy(scriptable);
                
                            return obj;
                        }
                    }
                }