informa
5 min read
article

Resource Manager and Object Pooling in Unity

An object pooling and resource managing pattern (no "Resources" folder is involved).

This blog was originally posted on my website: https://www.anton.website/resource-manager-and-object-pooling/

You know the Resources folder right? The place where you can put an asset and instantiate it whenever you need them with the Resources.Load<T>() function. Yeah, don’t use it. “But how should I instantiate the prefabs?” I hear you ask. Don’t worry! I will tell you about a very simple pattern I usually use for such purposes. And you will have a simple object pooling system as a bonus.

The first thing we are gonna need is the ResourceManager class. I will make it a Singleton, using this base class (use Singletons with caution though!).

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
    }
}

Next, we need a class to hold the prefab and a list of instantiated objects.

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
        [Serializable]
        private class ObjectHolder
        {
            [SerializeField]
            private GameObject prefab;
            [SerializeField]
            private List<GameObject> instantiated = new List<GameObject>();
        }
    }
}

We won’t need the ObjectHolder class outside of the ResourceManager, so I made it a private class. The [Serializable] part is important because we want to see what’s in it from the editor. The [SerializeField] attribute forces Unity to serialize private fields, making them viewable in the inspector. We want to set the prefab from the editor but don’t want anyone to actually access it so it is a perfect use case. Serializing the instantiated list is not mandatory but I will keep it for now. So let’s add a method to actually instantiate the prefab. I will call it Get().

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
        [Serializable]
        private class ObjectHolder
        {
            [Tooltip("The object to instantiate"), SerializeField]
            private GameObject prefab;
            [Tooltip("The pool of instantated objects"), SerializeField]
            private List<GameObject> pool = new List<GameObject>();

            /// <summary>
            /// Returns a game object from the pool. Instantiate a new one if none is available
            /// </summary>
            /// <param name="position">Position of the object</param>
            /// <param name="rotation">Rotation of the object</param>
            public GameObject Get(Vector3 position = default(Vector3), Vector3 rotation = default(Vector3))
            {
                for (int i = 0; i < pool.Count; i++)
                {
                    if(!pool[i].activeInHierarchy)
                    {
                        pool[i].transform.position = position;
                        pool[i].transform.rotation = Quaternion.Euler(rotation);
                        pool[i].SetActive(true);
                        return pool[i];
                    }
                }

                pool.Add(Instantiate(prefab, position, Quaternion.Euler(rotation)));
                return pool[pool.Count - 1];
            }
        }
    }
}

Finally, define an ObjectHolder field to the ReourceManager folder like this:

public ObjectHolder testPrefab;

Set the prefab from the inspector and you are good to go!

Instead of instantiating object like this:

Instantiate(prefab);

Instantiate it like this:

ResourceManager.Instance.testPrefab.Get();

Disable the game object whenever you are done with it and it will be ready to be reused.

This pattern will make managing prefabs much easier. In fact, if you drop the Singleton, you can have multiple resource managers for different scenes or states. I will add a couple more functions but this is the core of this pattern. Here is the final version:

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
        [Serializable]
        public class ObjectHolder
        {
            [Tooltip("The object to instantiate"), SerializeField]
            private GameObject prefab;
            [Tooltip("The pool of instantated objects"), SerializeField]
            private List<GameObject> pool = new List<GameObject>();

            /// <summary>
            /// Returns a game object from the pool. Instantiate a new one if none is available
            /// </summary>
            /// <param name="position">Position of the object</param>
            /// <param name="rotation">Rotation of the object</param>
            public GameObject Get(Vector3 position = default(Vector3), Vector3 rotation = default(Vector3), Transform parent = null)
            {
                for (int i = 0; i < pool.Count; i++)
                {
                    if (!pool[i].activeInHierarchy)
                    {
                        pool[i].transform.SetParent(parent);
                        pool[i].transform.position = position;
                        pool[i].transform.rotation = Quaternion.Euler(rotation);
                        pool[i].SetActive(true);
                        return pool[i];
                    }
                }

                pool.Add(Instantiate(prefab, position, Quaternion.Euler(rotation)));
                pool[pool.Count - 1].transform.SetParent(parent);
                return pool[pool.Count - 1];
            }

            /// <summary>
            /// Destroyes currently disabled objects
            /// </summary>
            public void DestroyUnused()
            {
                for (int i = 0; i < pool.Count; i++)
                {
                    if(!pool[i].activeInHierarchy)
                    {
                        Destroy(pool[i]);
                        pool.Remove(pool[i]);
                    }
                }
            }

            /// <summary>
            /// Destroyes all objects
            /// </summary>
            public void DestroyAll()
            {
                for (int i = 0; i < pool.Count; i++)
                    Destroy(pool[i]);

                pool.Clear();
            }

            /// <summary>
            /// Instantiates new objects to the pool
            /// </summary>
            /// <param name="count">Number of objects to prepare</param>
            public void Prepare(int count = 0)
            {
                for (int i = 0; i < count; i++)
                {
                    pool.Add(Instantiate(prefab));
                    pool[pool.Count - 1].SetActive(false);
                }
            }
        }

        //A transform to store inactive objects
        private static Transform pool;
        private static Transform Pool
        {
            get
            {
                if (!pool)
                    pool = new GameObject("Pool").transform;

                return pool;
            }
        }

        public ObjectHolder testPrefab;

        /// <summary>
        /// Disables the object and moves it under the Pool objecg
        /// </summary>
        /// <param name="obj">Object to disable</param>
        public static void Remove(GameObject obj)
        {
            obj.SetActive(false);
            obj.transform.SetParent(Pool);
        }
    }
}

You can download this project from Github.

So what do you think? Is it useful? Is there an alternative solution? Any suggestions? Optimizations? Let me know!

Latest Jobs

Treyarch

Playa Vista, California
6.20.22
Audio Engineer

Digital Extremes

London, Ontario, Canada
6.20.22
Communications Director

High Moon Studios

Carlsbad, California
6.20.22
Senior Producer

Build a Rocket Boy Games

Edinburgh, Scotland
6.20.22
Lead UI Programmer
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more