0

Usually, if you use object pooling, you make a singleton like in this video. After seeing this video, I discovered how messy singleton can be. Is there any other way to do object pooling without using singletons? I wanna instead use Events.

SushiWaUmai
  • 348
  • 6
  • 20
  • 1
    Did you want to explain how in the video _object pooling_ is related to _singletons?_ I'd rather not have to watch a 17 minute video with an advertisement embedded in it –  Jan 16 '21 at 04:16
  • No, just wanted to know a way to do object pooling that doesn't use singletons. – SushiWaUmai Jan 16 '21 at 04:37
  • The singleton is only used to have a lazy and easy way of getting the reference ... You can use any other way of getting a reference including a static class or ScriptableObject etc ... but most of the time it is then either tricky to set it up or you have to use some kind of similar pattern to singleton anyway (I'm speaking of e.g. `FindObjectOfType` which basically simply assumes a singleton pattern without actually implementing the singleton) – derHugo Jan 16 '21 at 09:16

1 Answers1

0

You would need to hold the pool in a class which is not a singleton, and handle your gameobject pool according to your events. Regarding to call them with events, "I want to use events" is not a very concrete question. You need to set your events to listen (method subscribe) and to call them in the code wherever they're supposed to occur, this is invoke the method. I suggest that if you are not clear about this, try to use the unity events (OnTriggerEnter, if(Input.GetMouseButtonDown(0)) in the Update etc) until you dig in the topic enough to understand them and make ones of you own with c# events or UnityEvents when needed.

Find two template scripts, a pool and and event handler to handle your objects in the scene. You can check those out in an empty scene with your respective two gameObject to attach, and the object you want in the pool, pressing 'space' and 'A' to create from pool and return to pool respectively.

Pool manager:

using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
    private Queue<GameObject> objPool;
    private Queue<GameObject> activeObj;
    private int poolSize = 10;
    public GameObject objPrefab;

    void Start()
    {
        //queues init
        objPool = new Queue<GameObject>();  
        activeObj = new Queue<GameObject>();
        //pool init
        for (int i = 0; i < poolSize; i++) 
        {
            GameObject newObj = Instantiate(objPrefab);
            objPool.Enqueue(newObj);   
            newObj.SetActive(false);    
        }
    }

    public GameObject GetRandomActiveGO() {
        GameObject lastActive = default;
        if (activeObj.Count > 0)
            lastActive = activeObj.Dequeue();
        else {
            Debug.LogError("Active object queue is empty");
        }
        return lastActive;
    }

    //get from pool
    public GameObject GetObjFromPool(Vector3 newPosition, Quaternion newRotation)
    {
        GameObject newObject = objPool.Dequeue();
        newObject.SetActive(true);
        newObject.transform.SetPositionAndRotation(newPosition, newRotation);

        //keep actives to be retrieved
        activeObj.Enqueue(newObject);
        return newObject;
    }

    //return to pool
    public void ReturnObjToPool(GameObject go)
    {
        go.SetActive(false);
        objPool.Enqueue(go);
    }
}

Event handler:

using UnityEngine;

public class EventHandler : MonoBehaviour
{
    public delegate GameObject OnSpacePressed(Vector3 newPosition, Quaternion newRotation);
    public OnSpacePressed onSpacePressed;

    public delegate void OnAKeyPressed(GameObject go);
    public OnAKeyPressed onAKeyPressed;

    public PoolManager poolManager;

    void Start()
    {
        onSpacePressed = poolManager.GetObjFromPool;
        onAKeyPressed = poolManager.ReturnObjToPool;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onSpacePressed?.Invoke(new Vector3(0, 0, 0), Quaternion.identity);
        }

        //here I get a random active, however this would be called in the specific objects remove circumstances, 
        //so you should have a reference to that specific gameobje when rerunrning it to the pool.
        if (Input.GetKeyDown(KeyCode.A))
        { 
            GameObject go = poolManager.GetRandomActiveGO();
            onAKeyPressed?.Invoke(go);
        }
    }
}

Edit: Singleton pattern

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T _instance;
    public static T instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = GameObject.FindObjectOfType<T>();
                if (_instance == null)
                {
                    _instance = new GameObject(typeof(T).Name).AddComponent<T>();
                }

            }
            return _instance;
        }
    }
}
rustyBucketBay
  • 4,320
  • 3
  • 17
  • 47
  • Hi, I appreciate your answer, but in this example, you still have to create a single instance of the PoolManager, which is a singleton. You still have to create a PoolManager for every single level, if you have several levels with the same object to pool. Is there any way to avoid that? For instance with a static class? – SushiWaUmai Jan 16 '21 at 14:40
  • I dont see your point. In the singleton pattern the singleton is actually static. Find the code of the singleton pattern updated in my answer. I thought that your question was due to the problem of considering not incurring in the [god object](https://en.wikipedia.org/wiki/God_object#:~:text=In%20object%2Doriented%20programming%2C%20a,pattern%20and%20a%20code%20smell.&text=In%20contrast%2C%20a%20program%20that,does%20not%20follow%20this%20approach.) pattern, so dividing your logic in different pools + to avoid the events that unity by default provides – rustyBucketBay Jan 16 '21 at 14:54
  • Is there is anyway do to object pooling without creating a gameobject? – SushiWaUmai Jan 16 '21 at 19:22
  • in unity monobehaviours 'live' inside gameobjects. You cannot do `MonoBehaviour myMonoBhvr = new MonoBehaviour()`. The instance of a monobehaviour needs a gameObject to be attached to. Strictly talking if you dont need any of the methods providede by the monobehaviout class you can avoid to inherit from it so creating the gameObject. There are workarounds even when you need some of the monobehaviour methods. However in the case of the polling or any functionality that you need to be available during the gameplay, creating the corresponding manager gameObject makes sense. – rustyBucketBay Jan 16 '21 at 19:52
  • So it is not possible to make an object pool without creating a gameObject? – SushiWaUmai Jan 22 '21 at 11:56
  • Technically you can, but I just dont see the point of it. For example you would not have the Unity Engine's functions available, so you would not be able to call `Instantiate(objPrefab)` as it would not be monobehaviour. You would need to create a dummy monobehaviour to call the instantiate which does not make many sense. Also you would need to get the reference to the pol gameObject to instantiate with the starting code, instead of having the `public` or `SerilizedField` in the editor directly. – rustyBucketBay Jan 22 '21 at 18:27
  • Also if you make your class static, you cannot declare instance member in static classes so no much chance to handle your pool effectively that way. – rustyBucketBay Jan 22 '21 at 18:32
  • Couldn't you get around with that with ```Object.Instantiate``` and ```[RuntimeInitializeOnLoadMethod]``` to create the pool? – SushiWaUmai Jan 22 '21 at 23:34
  • `Object ` is in the inheritance chain of `MonoBehaviour`, so if you do not create an object where your monobehaviour "lives in", you would not be able to call instantiate from there. – rustyBucketBay Jan 23 '21 at 11:34
  • That is why you would need to make a weird workaroud for the pooling pattern without a gameobject. I dont mind unswering, but you can try it yourself to get the idea and your own conclusions instead of keep on asking. Trying yourself you will get to the end of the issue, and be clear with your sdelf opinion regarding your approach worths it or not – rustyBucketBay Jan 23 '21 at 11:37
  • 1
    ok thank you very much, I will accept your answer. – SushiWaUmai Jan 24 '21 at 00:09