Pooling in unity3d – a simple tutorial

We’ve been through it: instantiations at runtime can be a huge problem. It can cause a spike in the workload of any game, and then you have to find ways to fix that when it happens. Or you can use a technique called pooling, like pros do.

you can have a lot of pools
you can have a lot of pools

What’s pooling in unity3d?

The same it should be anywhere else: instead of instantiating objects wherever and whenever you need them, you create a pool beforehand (while the loading screen is on) and then when it’s needed you grab an object from the pool.

To make a pool you’ll need 3 things:

  • a pool script
  • a pool interface
  • a pool host
    Let’s start with the trivial stuff first.

Pool interface iPoolable

It's objects in a pool, got it?
It’s objects in a pool, got it?

When an object is “in the pool” it’s better if it’s inactive in all conceivable ways. But of course the pool script should not be required to know how to inactivate every single object because that would be a maintenance nightmare. Therefore we’ll need this simple interface to be implemented by one script at the root of the pooled gameobject:

public interface iPoolable
{
  Pool source { get; set; }
  void Initialize();
  void Deactivate();
}

The source property is a reference to the pool script that holds the object’s pool, oviously. Why should the pooled object need to know that? So that it can return to the pool by itself by calling:

source.PutInPool(gameObject);

The Initialize function’s duty is to prep the object to enter the scene in an active state, just as if it was actually instantiated at the very end of the function.

The Deactivate function instead is called when the object would have been removed from the scene weren’t we pooling it. It has to minimize the impact on both memory and cpu/gpu for holding the object in the pool. This usually involves at least a SetActive(false) and a stop on all coroutines on the object if any, but one can get creative and also switch layers and stuff like that.

The pool host

This isn’t a strictly needed element for pooling to work, but if like me you don’t enjoy having a bunch of gameobjects floating around in your game’s hierarchy, you want to do this:

public class PoolHost : MonoBehaviour
{
    public static PoolHost instance;

    void Awake()
    {
        if (instance == null)
            instance = this;
        else
            Destroy(gameObject);
    }

    public static void Hold(GameObject poolable)
    {
        poolable.transform.parent = instance.transform;
    }
}

It’s just a singleton with one static function to hold the pooled gameobject inside the one this script is attached to. You could, of course, separate the pools by type, but why?

but why?

The actual Pool script

This script is designed to be used as a field in another script. This approach enables you to do all sorts of customizations, for instance your weapons can have a pool of bullets, so that you can make more weapons by changing the bullet prefab reference in the pool field.

[System.Serializable]
public class Pool
{
    Stack<GameObject> pool;
    [SerializeField]
    int initialPoolSize;
    [SerializeField]
    int expansionPoolSize;
    [SerializeField]
    GameObject prefabReference;

    public void Populate()
    {
        //stuff..
    }

    void ExpandPoolPopulation(int amount, GameObject prefabReference)
    {
        //stuff..
    }

    public GameObject Allocate()
    {
        //stuff..
    }

    public void PutInPool(GameObject handled)
    {
        //stuff..
    }
}

As you can see it’s entirely serializable, so that we may edit its parameters from the inspector when we use it as a field. The initialPoolSize variable is used to set how many gameobjects we should instantiate in the Populate call, while the expansionPoolSize indicates how many should be instantiated when the pool is empity and an allocation is requested.

Guess what the prefabReference is. Right! It’s where you drag-and-drop your prefab from the project explorer.

drag-and-drop

So, the idea is that the object using this pool needs to Populate it at first, then it can Allocate instances from it and when it’s done it (or the pooled object itself) can PutInPool back the pooled thingy.

Now, let’s see the single functions:

    public void Populate()
    {
        pool = new Stack<GameObject>();
        ExpandPoolPopulation(initialPoolSize, prefabReference);
    }

This one is damn easy. It initializes the pool variable with a brand new Stack (of course you can use my garbageless list instead!), then calls ExpandPoolPopulation to instantiate the very first objects in the pool.

    void ExpandPoolPopulation(int amount, GameObject prefabReference)
    {
        for (int j = 0; j < amount; j++)
        {
            GameObject result = GameObject.Instantiate(prefabReference);
            result.GetComponent<iPoolable>().source = this;
            PutInPool(result);
        }
    }

This one is really straightforward too. It just instantiates the prefab a number of times, sets the source for the object and then puts it in the pool. It’s as obvious as it gets.

    public void PutInPool(GameObject handled)
    {
        handled.GetComponent<iPoolable>().Deactivate();

        handled.SetActive(false);

        handled.transform.position = Vector3.zero;
        handled.transform.rotation = Quaternion.identity;
        handled.transform.localScale = Vector3.one;

        PoolHost.Hold(handled);

        pool.Push(handled);
    }

Here the first thing to do is to call the Deactivate function through the iPoolable interface so that it performs the object specific stuff, then we take care of the operations that can be done on every single gameobject, like moving it in the poolHost so that our hierarchy is clean. Obviously you don’t necessarily have to reset the transform values, but if that’s something that causes you problems, well, you are doing something very wrong.

Last thing we do, is to put the reference to the gameobject inside the pool stack, so that its ready to be used.

    public GameObject Allocate()
    {
        if (pool.Count <= 0)
            ExpandPoolPopulation(expansionPoolSize, prefabReference);

        GameObject result = pool.Pop();

        result.SetActive(true);
        result.GetComponent<iPoolable>().Initialize();
        return result;
    }

Now that we know how the stuff gets inside the pool we can look to how it’s taken out of it. The allocate function first of all checks wether the pool is empity or not. It it’s empity you need more stuff, so you have to expand it.
If this allocation is too much for you, just raise the initialPoolSize and put an error message here, but you are more likely to be better off this way.

Then it recovers the gameobject instance from the pool and initializes it so that it’s prepared just as if it was instantiated right now and returns it to whatever script called this function.

That’s all folks!

Thanks for the attention, copy-pasteable code is just ahead. Remember that the populate function is quite expensive so you’ll probably need to use it behind a loading screen, if you don’t need to then you probably don’t need pooling either.

For any question please don’t hesitate to contact me on my twitter account, and if you don’t want to miss my next tutorial consider subscribing to my Newsletter!

And by the way, I’m currently looking for a job, if you think you may have one for me, take a look at my portfolio!

public interface iPoolable
{
  Pool source { get; set; }
  void Initialize();
  void Deactivate();
}


public class PoolHost : MonoBehaviour
{
    public static PoolHost instance;

    void Awake()
    {
        if (instance == null)
            instance = this;
        else
            Destroy(gameObject);
    }

    public static void Hold(GameObject poolable)
    {
        poolable.transform.parent = instance.transform;
    }
}


[System.Serializable]
public class Pool
{
    Stack<GameObject> pool;
    [SerializeField]
    int initialPoolSize;
    [SerializeField]
    int expansionPoolSize;
    [SerializeField]
    GameObject prefabReference;

    public void Populate()
    {
        pool = new Stack<GameObject>();
        ExpandPoolPopulation(initialPoolSize, prefabReference);
    }

    void ExpandPoolPopulation(int amount, GameObject prefabReference)
    {
        for (int j = 0; j < amount; j++)
        {
            GameObject result = GameObject.Instantiate(prefabReference);
            result.GetComponent<iPoolable>().source = this;
            PutInPool(result);
        }
    }

    public GameObject Allocate()
    {
        if (pool.Count <= 0)
            ExpandPoolPopulation(expansionPoolSize, prefabReference);

        GameObject result = pool.Pop();

        result.SetActive(true);
        result.GetComponent<iPoolable>().Initialize();
        return result;
    }

    public void PutInPool(GameObject handled)
    {
        handled.GetComponent<iPoolable>().Deactivate();

        handled.SetActive(false);

        handled.transform.position = Vector3.zero;
        handled.transform.rotation = Quaternion.identity;
        handled.transform.localScale = Vector3.one;

        PoolHost.Hold(handled);

        pool.Push(handled);
    }
}

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Server maintenance gold

From:
http://meta.serverfault.com/questions/1986/what-are-the-canonical-answers-weve-discovered-over-the-years

These are the questions we have identified as Canonical:

Capacity Planning

Career

Datacenter Design

Documentation

EMail & Spam

Hardware

Hosting provider/server hardware shopping

Infrastructure Software

Licensing

Networking

Security

SSH

Terminal Server (RDS)

Uptime

Virtualization

Web Servers

Windows (General)

Meta

List of Canonical Topics that do not have a Q/A yet or need improvement:

Career

Datacenter Design

Infrastructure Software

Networking

Web Servers

  • Apache vHost
  • Apache .htaccess/overrides

Enterprise Storage

Create one or more Question that covers “ES” topics… not sure how this should be organized exactly. Merge these answers into that Question(s) (keeping them separate/complete answers).

Server Hardware

In response to Why The Hostility, I wrote this for consideration as a canonical Q/A:

What are the basics of running a Web Server?

my.cnf generation wizard:

https://tools.percona.com/wizard

Database table fragmentation command:

mysqlcheck -u root -p –auto-repair –optimize –all-databases

Ultimate Guide: Stop and Remove All the Spam and Other Junk Traffic in Google Analytics

Ultimate guide: How to stop and remove ALL the spam and other unnecessary traffic in Google Analytics

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •