Object pool And interface

Hi,
Sorry if my question seems weird. I’m quite new to coding and game development.
My first game includes a bunch of different objects which create and after a while destroy. As I heard instantiating and destroying objects constantly is so expensive so I used pooling for my objects and now I totally stuck :smiley:
In my game, if the user clicks the objects before 4 seconds it will be returned to the pool and if the player misses the click after 4 seconds it will be returned to the pool either but it should decrease one HP.

So before pooling, I’d used the timer on each of my game objects that damaged health, and in my click manager, I used destroy method so the HP system automatically worked.
But now as the object doesn’t destroy and is just disabled I don’t know how to fix it.

Are there any ways to use interfaces to fix it? For example, if I want to check bool (isActive ) for each object that spawns from pool in 4 seconds.

My pool script:

using System.Collections.Generic;
using UnityEngine;

public class ObjectPooler: MonoBehaviour
{
    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject Object;
        public int size;

    }

    public List<Pool> pools;
    public Dictionary<string, Queue<GameObject>> poolDictionary;

    void Start()
    {
        poolDictionary = new Dictionary<string, Queue<GameObject>>();

        foreach (Pool pool in pools)
        {
            Queue<GameObject> objectPool = new Queue<GameObject>();

            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.Object);
                obj.SetActive(false);
                objectPool.Enqueue(obj);
            }

            poolDictionary.Add(pool.tag, objectPool);

        }

    }

    public GameObject SpawnFromPool(string tag, Vector2 position, Quaternion rotation)
    {
        if (!poolDictionary.ContainsKey(tag))
        {
            Debug.LogWarning("Pool with tag" + tag + "doesn't exist.");
            return null;

        }

        GameObject objectToSpawn = poolDictionary[tag].Dequeue();

        objectToSpawn.SetActive(true);
        objectToSpawn.transform.position = position;
        objectToSpawn.transform.rotation = rotation;

      
        poolDictionary[tag].Enqueue(objectToSpawn);

        return objectToSpawn;
    }

    public GameObject ReturnObjectToPool(string tag, GameObject returnObject)
    {

        poolDictionary[tag].Enqueue(returnObject);

        returnObject.SetActive(false);

        return returnObject;

    }

}

For one, never use Unity pooling system… For the love of pete, make your own…

And secondly, I wouldn’t use Interfaces as they are slower than GetComponent is during runtime. I personally use static variables, or roughly Singletons, when it comes to script to script communication. A prime example I showed here : https://answers.unity.com/questions/1917831/performantly-handle-script-to-script-communication.html

But to make your own pooling system, all you need to know is how it works. You first make a List of the class, which is used to house your pool. And then simple checks, and Get calls will determine how many are active and if new ones need Instantiated(or just instantiate a bunch before hand). I think explaining this would take longer than just showing one of my current pooling setups :

    public class Master : MonoBehavior
    {
    	public static List<Magic> allMagics = new List<Magic>();
    	public static List<MagicBall> magicBalls = new List<MagicBall>();
    	
    	public virtual Magic GetMagic(Magic spell, Vector3 origin)
        {
            print("GetMagic failed");
            return null;
        }
    }
    
    public class MagicBall : Magic
    {
    	void Awake() 
    	{ 
    		allMagics.Add(this);
            magicBalls.Add(this);
            SetMagicComponents();
            spellID = 1;
            gameObject.SetActive(false);
    	}
    	
    	private void OnEnable()
        {
            CountSpells(this);
        }
    
        private void OnDisable()
        {
            CountSpells(this);
        }
    }
    
    public class Magic : Master
    {
    	public GameObject magicBall;
    	public static int activeMagicBalls;
    	
    	private void Update()
        {
            if (magicBalls.Count == activeMagicBalls) { AddSpell(magicBall); }
        }
    	
    	public override Magic GetMagic(Magic spell, Vector3 origin)
        {
            if (spell.spellID == magicBalls[0].spellID)
            {
                for (int i = 0; i < magicBalls.Count; i++)
                {
                    if (!magicBalls[i].gameObject.activeInHierarchy)
                    {
                        magicBalls[i].gameObject.SetActive(true);
                        magicBalls[i].trans.position = origin;
                        magicBalls[i].rigid.velocity = Vector3.zero;
                        magicBalls[i].rigid.angularVelocity = Vector3.zero;
                        return magicBalls[i];
                    }
                }
            }
    
            print($"GetMagic failed");
            return null;
        }
    
        public void AddSpell(GameObject obj)
        {
            Instantiate(obj, Vector3.zero, Quaternion.identity, transform);
        }
    
        public void CountSpells(Magic spell)
        {
            if (spell.spellID == magicBalls[0].spellID)
            {
                activeMagicBalls = 0;
                for (int i = 0; i < magicBalls.Count; i++)
                {
                    if (magicBalls[i].gameObject.activeInHierarchy)
                    {
                        activeMagicBalls++;
                    }
                }
                return;
            }
    
            print($"Magic.CountSpells - call {spell} - reached the end, and shouldn't have!");
        }
    }

I may have missed something? Not sure… But you make a list of the class, when the object is made it sends itself to the list. Then you use a “Getter” which iterates through that list, finds and returns one of the objects that is !activeInHeirarchy and returns it while making it active again and setting the new position(simulating Instantiate).

Once the object is “used” or disabled(destroyed), or when it is created it recalculates the int number of how many are active(since calling the list.Count will only show how many exist, not how many ready to pull from pool).

Also, in OnEnable of magicBall is where you would also say things like health = maxHealth, or lifeTime = 0;, etc…

I may have confused you, so please ask a question on this(comment), and I will continue to help until you get it all figured out
:slight_smile:

Wow, forgot all about that virtual method, but I have since improved the method:

public class Master : MonoBehavior
{
	public GameObject ballPrefab; // set in inspector, or through code
	public GameObject spikePrefab; // set in inspector, etc...
	
	public static List<Magic> allMagics = new List<Magic>();
	public static List<Magic> allMagicBalls = new List<Magic>();
	public static List<Magic> allMagicSpikes = new List<Magic>();
	
	public Magic GetMagicFromList(List<Magic> list, Vector3 position, GameObject prefab)
    {
		// if list already contains objects, check for unused object
        if (list.Count > 0)
        {
            for (int i = 0; i < list.Count; i++)
            {
                if (!list[i].gameObject.activeSelf)
                {
                    list[i].transform.position = position;
                    list[i].gameObject.SetActive(true);
                    return list[i];
                }
            }
        }
		// if no object returned from pool, create a new one
        Instantiate(prefab, position, Quaternion.identity);
		// object Awake() adds itself to list during Instantiate() call
		// then the appropriate script is returned by index using list.Count
        return list[list.Count - 1];
    }
}
public class Magic : Master
{
	// any variables set in parent script can be easily called
	// through any function that calls <Magic> in general
	public int spellID;
	public float damage;
	public float manaNeeded;
}
public class MagicBall : Magic
{
	void Awake() 
	{ 
		allMagics.Add(this);
        allMagicBalls.Add(this);
		spellID = 1;
		damage = 125f;
		manaNeeded = 30f;
	}
	
	// Other code, including movement and speed...
}
public class MagicSpike : Magic
{
	void Awake() 
	{ 
		allMagics.Add(this);
        allMagicSpikes.Add(this);
		spellID = 2;
		damage = 160f;
		manaNeeded = 60f;
	}
	
	// Other code, including movement and speed...
}
public class Player : Master
{
	// Other code...
	
	void Update()
	{
		if (shootMagicBall == true)
		{
			MagicBall currentMagic = (MagicBall)GetMagicFromList(allMagicBalls, shootPosition, ballPrefab);
			currentMagic.damage += 10f; // if adding more than default damage
			myMana -= currentMagic.manaNeeded; // if subtracting variables from player
			shootMagicBall = false;
		}
		
		if (shootMagicSpike == true)
		{
			MagicSpike currentMagic = (MagicSpike)GetMagicFromList(allMagicSpikes, shootPosition, spikePrefab);
			currentMagic.damage += 25f;
			myMana -= currentMagic.manaNeeded;
			shootMagicSpike = false;
		} 
		// etc...
	}
	
	// Other code...
}

Since the call to the pool, now is more generalized, you only need one “Get” function for any children within that parent category. Since the lists being used are static, any function call with them doesn’t make any duplicates(garbage collection). And since everything uses Inheritance, you can easily call a parent function through any child.

Also need to add when magic hits something or is done, that it (gameObject.SetActive(false)), so it returns to pool. do not destroy it, just turn it off from being active in scene.

I do recommend making a class to hold all the objects used for prefabs(ObjList.cs) which is separate from other children from master, so those don’t show in Inspector. This code is purely an example, and will need logical fine tuning. But hopefully the idea comes across correctly, Cheers!