Gamasutra: The Art & Business of Making Gamesspacer
View All     RSS
April 25, 2019
arrowPress Releases








If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 

Resource Manager and Object Pooling in Unity

by Anton Semchenko on 04/12/19 10:39:00 am

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

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!


Related Jobs

Wizards of the Coast
Wizards of the Coast — Renton, Washington, United States
[04.24.19]

Sr Lead Software Engineer - Arena
Wizards of the Coast
Wizards of the Coast — Renton, Washington, United States
[04.24.19]

Lead Software Engineer - Arena
FoxNExt Games
FoxNExt Games — San Jose, California, United States
[04.24.19]

Infrastructure Engineer
FoxNExt Games
FoxNExt Games — San Jose, California, United States
[04.24.19]

Console Gameplay Engineer





Loading Comments

loader image