3
\$\begingroup\$

I am using Unity3D and I made a super simply pooling system to reuse a specific object. My only concern is I am not sure if I am doing it the best possible way. If I were doing this in C++ there would be a bunch of pointers and be passing pointers to the objects.

Now in C# I understand many things are hidden as pointers. So, I am unclear whether the things if I should just return things, thinking it is by value or do it by reference in C#'s way of using 'ref'.

What I mainly want to achieve is preventing me from duplicating the objects, but rather passing the memory address of where my objects are.

Hoping someone could provide some insight.

Here is the code: (probably most interest would be in GetObject and ReturnObject methods

using UnityEngine;
using System.Collections;

public class PoolingSystem : MonoBehaviour
{
    [SerializeField]
    // Keeping track of which units to create and put into a pool
    private GameObject _object;

    // Root container for the spawned objects given in the inspector
    private GameObject _root;

    [SerializeField]
    // Number of objects to keep track for in the pool
    private int _num_obj;

    // Number of objects currently in use
    private int _num_used;

    // Keep track of _pooled objects
    private GameObject[] _pool;

    // Keep track of which index in the _pool is being used
    private bool[] _in_use;

    // Default vector to move objects away from the scene
    private Vector3 _default;

    /// <summary>
    /// Gets the object in which the pooling system is focusing on
    /// </summary>
    /// <value>The object.</value>
    public GameObject pool_obj
    {
        get { return _object; }
    }

    /// <summary>
    /// Gets the total amount of objects this pooling system should restrict to.
    /// </summary>
    /// <value>The total.</value>
    public int total
    {
        get { return _num_obj; }
    }

    /// <summary>
    /// Gets the current number of objects in use by the pool system for the specific object
    /// </summary>
    /// <value>The number_in_use.</value>
    public int number_in_use
    {
        get { return _num_used; }
    }

    void Awake ()
    {
        _pool      = new GameObject[_num_obj];
        _in_use    = new bool[_num_obj];

        _root      = new GameObject();
        _root.name = string.Format("PoolingSystem: {0}", _object.name);

        _num_used  = 0;

        _default   = new Vector3(9999,9999,9999);

        for(int i=0;i<_num_obj;++i)
        {
            // Create specified object
            _pool [i]  = Instantiate(_object, Vector3.zero, Quaternion.identity) as GameObject;

            // Mark object not in use
            _in_use[i] = false; 

            // Parent the pooled objects into root
            _pool[i].transform.parent = _root.transform;

            // Turned off specific components of the object
            Shutoff(ref _pool[i]);
        }
    }

    // Just some components to turn on
    void ReadyToDeploy(ref GameObject unit, Vector3 location)
    {
        unit.SetActive(true);
        unit.transform.position = location;
    }

    // Just some components to turn off
    void Shutoff(ref GameObject unit)
    {
        unit.transform.position = _default;
        unit.SetActive(false);
    }

    /// <summary>
    /// Gets an object of the desired type. If there is an object to spare return the object, else return null.
    /// </summary>
    /// <returns>The object.</returns>
    /// <param name="location">Location.</param>
    public GameObject GetObject(Vector3 location)
    {
        GameObject obj = null; 

        for(int i = 0; i < _num_obj; ++i)
        {
            if(_in_use[i] == false)
            {
                obj = _pool[i]; 
                _in_use[i] = true;
                _num_used  += 1;
                ReadyToDeploy (ref obj, location);
                i = _num_obj;
            }
        }

        if(obj == null)
        {
            Debug.LogWarning(string.Format("PoolSystem: object -> {0} has met max limit.", obj));
        }

        return obj;
    }

    /// <summary>
    /// Returns the object back to the pooling system. Returns true if it was sucessful in putting it back, else false.
    /// </summary>
    /// <returns><c>true</c>, if object was returned, <c>false</c> otherwise.</returns>
    /// <param name="obj">Object.</param>
    public bool ReturnObject(GameObject obj)
    {
        bool recieved = false;

        if(obj.tag == this._object.tag)
        {
            for(int i = 0; i < _num_obj; i++)
            {
                if(_in_use[i])
                {
                    _pool[i] = obj; 
                    Shutoff (ref _pool[i]);
                    _in_use[i] = false; 
                    _num_used -= 1;
                    i = _num_obj;
                    recieved = true;
                }
            }
        }
        else
        {
            Debug.LogError(string.Format("Parameter: {0} is type {1} which is not the same type as '{1}'", obj.name, obj.tag, this._object.tag));
        }

        return recieved;
    }
}
\$\endgroup\$
2
2
\$\begingroup\$

Classes, such as your GameObject are passed by reference (i.e. as a pointer) by default. No duplication happens and you do not need a keyword for that. C# handles it all for you.

Structs and primitive types such as Vector3 or int are value types which are passed by value by default and are therefore copied when you pass them into functions.

Therefore you can remove the ByRef keywords whenever you're passing your pooled object around for clarity at the least, and do not have to worry about duplicated objects unless you're passing a struct (which are meant to be small anyway) or a primitive type (which again, should be small).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.