OBJECT POOLING in Unity

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Here is another flavored one I created for my game, a little more generic one using generics instead of strings etc.

public abstract class PollingPool<T> where T : Component
{
    private readonly T prefab;

    private readonly Queue<T> pool = new Queue<T>();
    private readonly LinkedList<T> inuse = new LinkedList<T>();
    private readonly Queue<LinkedListNode<T>> nodePool = new Queue<LinkedListNode<T>>();

    private int lastCheckFrame = -1;

    protected PollingPool(T prefab, int preWarm = 0)
    {
        this.prefab = prefab;
        if(preWarm > 0)
            foreach(var item in Enumerable.Range(0, preWarm).Select((i, index) => GameObject.Instantiate(prefab)))
            {
                item.gameobject.SetActive(false);
                pool.Enqueue(item);
            }
    }

    private void CheckInUse()
    {
        var node = inuse.First;
        while (node != null)
        {
            var current = node;
            node = node.Next;

            if (!IsActive(current.Value))
            {
                current.Value.gameObject.SetActive(false);
                pool.Enqueue(current.Value);
                inuse.Remove(current);
                nodePool.Enqueue(current);
            }
        }
    }

    protected T Get()
    {
        T item;

        if (lastCheckFrame != Time.frameCount)
        {
            lastCheckFrame = Time.frameCount;
            CheckInUse();
        }

        if (pool.Count == 0)
            item = GameObject.Instantiate(prefab);
        else
            item = pool.Dequeue();

        if (nodePool.Count == 0)
            inuse.AddLast(item);
        else
        {
            var node = nodePool.Dequeue();
            node.Value = item;
            inuse.AddLast(node);
        }

        item.gameObject.SetActive(true);

        return item;
    }

    protected abstract bool IsActive(T component);
}

Subclass

public class AudioSourcePool : PollingPool<AudioSource>
{
    public AudioSourcePool(AudioSource prefab) : base(prefab)
    {
    }

    protected override bool IsActive(AudioSource component)
    {
        return component.isPlaying;
    }

    public void PlayAtPoint(AudioClip clip, Vector3 point)
    {
        var source = Get();

        source.transform.position = point;
        source.clip = clip;
        source.Play();
    }
}
👍︎︎ 7 👤︎︎ u/MDADigital 📅︎︎ Feb 12 2018 🗫︎ replies

Great video. Unlike most Object Pooling tutorials this doesn't skip the very important detail of resetting your objects.

I'm using a slightly different approach. Instead of a IPoolableObject interface, I have a PooledObject component that looks like this:

/// <summary>
/// Attaching this to a prefab will allow it to be pooled. On awake, it will
/// make a list of components that require a reset after pooling.
/// </summary>
public class PooledObject : MonoBehaviour {

    public int startPoolSize = 1;
    public int maxPoolSize = 10;
    [System.NonSerialized]
    public Transform poolingContainer;
    [System.NonSerialized]
    public bool isFromPool = false;

    private IPoolableComponent[] poolableComponents;
    // PooledObject handles resetting the transform scale
    private Vector3 normalScale;

    private void Awake()
    {
        poolableComponents = GetComponentsInChildren<IPoolableComponent>();
        normalScale = transform.localScale;
    }

    public void Decommission()
    {
        if(isFromPool)
        {
            transform.parent = poolingContainer;
            gameObject.SetActive(false);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    public void Recommission()
    {
        transform.localScale = normalScale;
        for (int i = 0; i < poolableComponents.Length; i++)
        {
            poolableComponents[i].Recommission();
        }
    }

}

Then individual components can define themselves as IPoolableComponent:

/// <summary>
/// Any monobehaviour that needs to have its state reset prior to returning to 
/// an object pool.
/// </summary>
public interface IPoolableComponent {

    /// <summary>
    /// This method will be called on the component when it is activated coming out of the pool.
    /// </summary>
    void Recommission();
}

Advantages:

  • Component based pooling. You define components that know how to pool themselves and can be freely mixed and matched. You can also create poolers for built-in Unity components (e.g., IParticlePooler). Then mix and match without having to duplicate your pooling logic.
  • You can set your start and max pooled objects on the prefab.
👍︎︎ 4 👤︎︎ u/justkevin 📅︎︎ Feb 12 2018 🗫︎ replies

Another great video from Brackeys.

I wonder if this only impacts lag spikes from the garbage collector or if it has an average fps impact as well.

👍︎︎ 2 👤︎︎ u/MengKongRui 📅︎︎ Feb 12 2018 🗫︎ replies

At last, I've been waiting for brackeys to upload object pooling.

👍︎︎ 2 👤︎︎ u/birdoutofcage 📅︎︎ Feb 12 2018 🗫︎ replies

I have a question about Pooling. This all looks easy and useful if I want to spawn and delete 100 cubes per second. Sure why not reuse them.

Now what if I have complex Class of Soldiers that have a lot of variables and references. Just "setEnabled(false)" and then true again, will be the "pooling process". But I need to reset all the values by myself, right? Restore everything to what it looks like when I instantiate a new instance for example.

👍︎︎ 2 👤︎︎ u/kyl3r123 📅︎︎ Feb 12 2018 🗫︎ replies

A small cherry on the top would be implementing a pool with a stack instead of queue or list. It makes much more sense and it uses temporal locality.

👍︎︎ 1 👤︎︎ u/IcyHammer 📅︎︎ Feb 13 2018 🗫︎ replies
Captions
in this video we'll learn how to spawn a bunch of objects without spending a lot of processing power we'll do this using a method called object polling we'll get into what it is and why it's useful in just a sec but first this video is sponsored by skillshare skillshare is an online learning community with more than 18 000 quality classes on tech design and more and it's actually something that i personally use and love i recently came across this awesome course on the fundamentals of creating pixel art for games and i definitely recommend you check it out a premium membership gives you unlimited access to all classes for less than 10 a month so to get started simply click the link in the description and the first 500 people to sign up will get their first 2 months for only 99 cents and with that let's get into the video so when making a game you often want to spawn in objects while playing of course unity has a built-in function for this called instantiate but using this function is not very performant in many cases it's much better to use an object pole this essentially means that when the game starts we create a bunch of inactive game objects in other words we make a pool of objects when we then want to spawn one of these objects instead of creating the object from scratch we simply take one of the objects from the pool enable it and put it in the right place if we run out of objects in the poll we can either decide to grow it or reuse older objects alright with that explanation let's jump right into unity so here in unity i've set up a very simple scene there's really only a ground and a light and a bit of image effects in here if we hit play you can see that i've set up this cube spawner that will just spit out a bunch of cube objects and that's pretty much all that's happening here you'll notice as this go it spits out more and more cubes and slowly fills up our hierarchy and it will keep going until unity crashes if we have a look at how this works it's actually very simple i have a cube spawning object and on this object i have a cube spawner script all the script does is that every fixed update call it instantiates a cube and the cube object is here it's just a rigid body with a cube script attached and all the script does is calculate a random force that it then adds to the rigid body so this is a good example of a scene where we can add object polling to make it more performant but before we start writing our script there are a few things that we need to understand so basically what we want to be able to do is create different object poles here we have three poles pull one two and three and we want each of these poles to be responsible for storing objects of a certain type we might have the first ones for circles the second one store squares and you get the idea and i think the best way to go about this is actually by using a dictionary because the dictionary allows us to create as many poles as we want to and associate each pool with some kind of tag in our case it's fairly simple we could take the first poll and associate it with circles and second one with squares and the third one with well something what this allows us to do is encode access one of these pools using its tag so if i wanted to access the squares i'll simply put that in and this would then give us the square pole then i would store each of these objects inside of the pool in what we call a queue the good thing about a queue is that it's basically like a list but it's very very fast to get the first item and we can also easily add new items to the end of the queue we can imagine why this is useful because if we wanted to go ahead and instantiate or spawn one of these objects we would simply take it from the queue and put it inside of our world and we would actually keep doing this until the pole gets emptied the smart thing about using aq in this instance is that if all of our objects are actually currently active in the world but we can still go ahead and grab the first one in the queue and that's just going to be the beginning object so each time we move them from the poll into the world we simply re-add them to the queue and so if we wanted to go ahead and add a new object say up here we would simply take the oldest object in the world which is this one and move it from that position into the new one and we could just keep doing that forever so now that we have a good understanding of how dictionaries and cues can be used to our advantage it's time to start scripting it out to do that let's create a new empty object in our hierarchy let's reset the transform on it and let's call it object polar let's just drag it to the top so we can always see it let's hit i component and let's create a new script called object polar as well we then double click it to open it up in visual studio i'm going to remove the update method here we won't be using it and i'm then going to go to the very top here and i'm going to create the dictionary so we'll write public dictionary and here the dictionary takes two types the first one is going to be the tag that we want to associate each pool with this is also referred to as the key and this is going to be of type string second we want to have the actual poll and remember we wanted to store that as a queue so we'll go ahead and create a queue here and whenever we create a queue we also need to tell unity what we want to store in the queue so we want to go ahead and store a queue of game objects and we'll call this dictionary poll dictionary remember whenever you're using dictionaries and cues you want to make sure to be using system.collections.generic so now in our start method we can set poll dictionary equal to a new dictionary and here we can use autocomplete to make it just fill out all of the types so now we have a new empty dictionary to work with and it's time to start filling it up with pools of objects but instead of doing this manually through code let's have some way of configuring what polls we want inside of the inspector to do this let's go to the top here and let's create our own class called poll so we'll create a public class we'll call it poll and we now choose what we want to store in each poll first of all we want to have a string with the tag of the poll we'll then have a public game object which is going to store the prefab of the objects in that pool and finally we'll have a public int which is going to store the total size of our pool that means at which point are we going to start reusing objects instead of spawning in new ones and to make sure this will show up inside of the inspector we have to mark it with an attribute called system.serializable so now we've created this class and let's go ahead and make a list so we'll create a public list of polls that we'll call pools if we now save this and head into unity we now see a list of polls show up and we can go ahead and add entries to this so i'm just going to add a single one and now for the first element here we can define a tag i'm going to tag this one as cube as the prefab i'm going to drag in my cube prefab and as the size here i'm going to set that to something like 150. so we'll only have a maximum of 150 active cubes at a time so now that we have our list of poles we need to start adding them to the dictionary to do that we want to loop through all of the pools so we'll go for each poll and we'll call each item poll in our polls list we want to go ahead and create a queue of objects so we'll create a queue here of game objects we'll call it our object pool and we'll set it equal to a new queue of game objects and now we want to go through and create each one of these objects so we'll create another for loop here and here we want to keep looping as long as i is less than pole dot size in other words we want to make sure that we fill out our entire poll by instantiating as many objects as we've defined in the size so now for each one of these we'll instantiate poll dot prefab and we'll store a reference to this object that we just created so gameobject we'll call it object and now we can go object.setactivefalse to make sure to disable it so we can't actually see it yet and finally we can add it to the end of our queue to do this all we need to write is object poll dot in q and then we'll feed it our object and that's really all so for each pool that we want to create we create a queue full of objects we make sure to add all the objects that we want to add to the queue and finally we want to add this pool to the dictionary so we'll go pull dictionary dot add and here we first want to give it the tag so pull.tag and then our pool of objects so object pull awesome so now we should actually see that if we save this go into unity and hit play it immediately spawns 150 cubes that are deactivated in our hierarchy these are of course not the cubes that we're seeing here they are added on top by the cube spawner but we'll go ahead and change that soon first we need to add functionality for taking some of these inactive cubes and spawning them into our active world to do that we'll create a new public void and we'll call it spawn from pull this is first going to take in a string with a tag which is of course the tag of the object that we want to spawn and we also want to take in a vector3 storing a position this is where we want to spawn our object as well as a quaternion for the rotation now here we can get the prefab that we want to spawn by simply going pull dictionary and then feeding it our tag just like i showed with the squares in the example so this now gives us the appropriate cue and then all we need to do is go dot dq in order to pull out the first element in the queue we then store this object so we'll create a game object and we'll call it object to spawn and set it equal to that object and now we can configure our object to spawn so we can set it to active so enable it we can also set its position optic to spawn.transform.position is going to be equal to position object to spawn.transform.rotation is going to be equal to rotation so we've actually now taken the object that we want to spawn set it to active moved it to the appropriate place in the world and so it should be showing and working just fine but we also want to remember to add it back to our queue so that it can be reused later to do that we simply go pull dictionary and we remember to pass in the tag dot nq and we feed it the object to spawn now there are still some things that we can add here to make it a tiny bit safer and easier to use one of these is we can go to the top here and check if poll dictionary dot contains key and this is just to make sure that if we give it a tag that it doesn't have a pool for we don't go through and cause any errors so if this is not true well then we simply want to go ahead and throw a debug.log warning saying that the pull with tag and then we feed it the tag doesn't exist there we go and then we can simply return out of the function a really nice feature of instantiate that i use a lot so when we go and instantiate an object is that it returns the object that we just created so let's do the same thing here we'll mark the return type as game object and right at the end of the function here we'll return the object to spawn so now we can always grab it from the place where we spawn it and here we simply want to return null in the case that there is no object so that should actually complete the main functionality of our object polar script but we still need a easy way to access it from within our cube spawner to do that we'll use a very simple singleton pattern now this is not a true singleton it's just a quick workaround and if you've never heard of singletons before or if you want to read more about them i'll make sure to have a link for that in the description all we're doing here is just very easily allowing ourselves to grab the object puller from the cube spawner so i'm just going to write a public static object polar which we'll call instance and then we'll create an awake method and in here we'll simply set instance equal to this and just to let everyone know that this is an attempt at a singleton we'll write singleton here and we'll end the region down here this doesn't do anything it just allows us to collapse the code like this and we don't have to look at it anymore so now we can save that and now when we go into the cube spawner what this allows us to do is actually delete the variable right here delete the instantiate call and instead we can go object polar dot instance dot and now we can simply call the function from in here so we'll call spawn from pool and then we can feed it a tag and we'll use the tag cube we can feed it a position we'll just use transform.position as well as a rotation we'll use criterion.identity and there we go if you're going to be calling this a lot like i'm doing here i would recommend storing this in a variable so we can just do that very quickly we'll create an object puller call it object pooler and then inside of the start method we can set object pooler equal to optic.instance and now we can simply reference it here that just makes things a tiny bit more performant so if we now save this go into unity and hit play we should see that after we spawn 150 cubes it stops but it hasn't actually stopped it's still reusing cubes but for some reason they aren't really moving anywhere they're just all stacked on top of each other the reason for this is that our cube script is currently set up to only apply a force in the start method and start is only called once when the object is actually instantiated so what we need to do is create some kind of way in order to notify this object that it has just been reused in other words we need to create our own start method and a fairly easy way to do this is using what we call an interface now if we go into a project we can go ahead and create a c-sharp script because this is going to be an interface we'll start it with an i for interface and we'll then call it pold object let's open it up in visual studio we don't want this to derive from mono behavior we want to remove both of the functions we can basically remove all of the namespaces and we don't want this to be a class we want it to be an interface now basically what an interface does is allows you to specify some types and functions that all objects that derive from this interface have to implement so in our case we can just write void on object spawn and we'll just close it off like this that's pretty much all we need to write inside of our interface if we now save this and go into our cube script we want to make sure that our cube derives from mono behavior and and this is where we put a comma i pulled object and you will see here that it now lights red and the reason why is that well now this class derives from i pulled object but it doesn't implement this on object spawn function so we need to make sure to go in here and instead of using start we want to use on object spawn and we'll also want to mark it as public and right away we can see that it no longer underlines with red because we're now implementing the function what we can then do is save this and go into our object polar go down to where we actually spawn an object from the pole and in here we can now get a component on the object that we're spawning so object to spawn dot get component of type i pulled object that's right we can actually search for interfaces we can installed this in a variable so i pulled object and we'll call it pooled object like this now it's not certain that we want all objects to have a script that derives from this interface we want that to be optional so we'll go in here and write if pulled object is not equal to null well then we can actually call a function on this interface and we of course want to go ahead and call on object spawn so this should in fact search for the interface check if it's not equal to null and if it isn't it will go in access the interface look for an implementation of this method and then inside of the cube actually call it and execute all the code so if we now save this go into unity and play we can see that cubes are now being properly reused yay definitely play around with this and have fun with it you can easily go in and add multiple pools we could go ahead and try and increase the size here to 300 as well and check out the results of course this object pulley could be made more performant one of the things that i would do is definitely try and get rid of the get component call so definitely have fun with it yourself also a cool guy from the community sent me over this script that he has made you can check that out on github and also just quickly search the asset store and found this plugin that is currently free and looks like it has most of the functionality that you're going to need i'll of course have links for both in the description that's pretty much it for this video make sure to check out skillshare simply click the link in the description for your chance to get a discount on that thanks for watching and i will see you in the next video thanks to all the awesome patreon supporters who donated in january and a special thanks to sean carry diego geick duderman diane gein biffio infinity ppr urai omer cyborg mummy derek keemsgerk myrrh facebook marify theodor dye john ramirez doubletap 45 james p superman the great john brewer guard jason detito alex rokitsky jan phil knapp suny jacobson james rogers robert brundt rob fern and erasmus
Info
Channel: Brackeys
Views: 327,512
Rating: undefined out of 5
Keywords: brackeys, unity, unity3d, how, to, howto, learn, course, tutorial, tutorials, tip, game, development, develop, games, programming, coding, basic, basics, C#, object, pooling, performance, spawn, spawning, objects, fast, pool, enhancement, optimization, optimisation, optimise, optimize, enhance, quick, performant
Id: tdSmKaJvCoA
Channel Id: undefined
Length: 17min 23sec (1043 seconds)
Published: Sun Feb 11 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.