Object Pooling in Unity

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
you're probably familiar with the instantiate and destroy methods used to add and remove game objects in unity in general they're well optimized and only have a small impact on performance but what would happen if you're firing a machine gun had endless waves of enemies you might be making hundreds of calls to instantiate objects and on top of that you need to destroy everything eventually now the insignificant cost of instantiating and destroying objects is starting to add up and slow down your game in this video we'll talk about when and how to use object pooling in your games as well as covering some of the pitfalls you may run into before we get started be sure to hit the like button and make sure you're subscribed instead of instantiating an object right when you need it and destroying it when you're done with it object pooling lets you instantiate a bunch of objects and store them until you need one this improves the performance during gameplay by doing most of the work before the game even starts one of the most common uses for object pooling is when you need to spawn a lot of projectiles but it can also be useful for something like obstacles in an endless runner or even enemies object pooling can be useful in any situation where you'll be reusing the same type of object over and over it can also help prevent memory fragmentation since it grabs a chunk of memory at the start of the game and defines a fixed location for all of the objects in the pool but this does mean you can end up wasting memory on unused objects i have a simple character controller set up to move around and when i press the spacebar we get a message in the console but instead of that we want to fire a projectile for now let's just use instantiate and destroy like you normally would and then we can look at how you can switch from that to object pooling first we need a prefab to instantiate in this case i just need a simple projectile and i'll add a rigidbody to it so i can add a force to make it move later creating a prefab of the projectile lets me easily spawn copies of it i'll also add a script to despawn the projectiles after so much time i'll add a despawn method and then use a code routine that will delay for the lifetime of the projectile before calling the dspawn method i'll spawn the projectiles from the player controller script that i already created first we need a reference to the prefab that we want to make copies of so let's add that in now in the onfire method i'll instantiate a new projectile and add a force to make it move if you're trying to spawn something besides a projectile you'll likely put this in some sort of spawn manager where you can check some conditions before spawning the object make sure you fill in the reference to the prefab in the inspector now if we play the game when i press spacebar it spawns a projectile in front of the player and it'll start flying away we can see the clones of the projectiles being created and destroyed in the hierarchy and this is exactly what we want to avoid doing so let's look into making an object pool let's start by creating the object pool script if you want you can treat each object pool as a component by leaving it as a monobehaviour i don't really like to do that especially if you're going to have multiple object pools because you'll have to remember where each one is and it can start to make the project messy so i'll remove the mono behavior and show you another way to handle the object pools later in its most basic form an object pool is just a list that holds a bunch of copies of whatever object you want to pool so we need to add a list to hold all of our objects to create the object pool we need to tell it what object we want and how many it's going to hold so let's add in some serialized field we want one to tell the pool what object we want and another to specify how many copies the pool will have and with those parts we can initialize the object pool to initialize it we'll instantiate the number of objects we specify and disable them as they're created the cpu is doing most of the work here instead of while you're playing the game now we can replace the projectile prefab in the player controller script with the projectile pool we want to be able to create object pools from the inspector so let's make sure the object pool class is serializable since i removed the projectile prefab i'm going to comment out the spawn logic for now to get rid of any errors so we can look at creating an object pool from the inspector since the object pool is serializable we can just drag in the projectile and tell it how many we want to create now we need to initialize the pool so i'll call the initialize method from start in the player controller but i need to make it public first and fix my typos so let me do that if we run the game now we should see 10 projectiles show up in the hierarchy we don't have a way to actually use any of these projectiles yet so let's look into how to spawn an object from the pool to spawn objects we need to keep track of where we are in the list so let's make an index that tracks which objects have already been used now we can make the spawn method typically you're going to want to do something with the object after it's spawned so we'll make sure to return the game object from this method now to spawn an object we just need to enable it and return the same one that we just enabled we also need to remember to increment the index so we don't keep reusing the first object in the list but now we'll be returning the wrong object so let's make a temporary variable to hold the object that's being spawned and then we can return it after we increment the index this is where we're going to run into the first problem we're eventually going to use all of the objects that are in the list so we want a way to wrap around back to the beginning of the list and reuse the old objects the modulo operator is really useful for this by incrementing the index and then performing a modulo with the number of objects in the list it automatically goes to the beginning of the list for us we can go back to the player controller script and fix the onfire method to work with the object pool to do that we just need to replace the instantiate call with a call to the spawn method we just created and then we need to make sure we set the position of the new projectile too since instantiate used to handle this for us if you wanted to you could add another spawn method to the object pool that takes a position as a parameter just like instantiate does this is where i realized i forgot something important when initializing the object pool so let's go fix that what i forgot is to actually add objects to the list so we need to add each of the objects as they're created during initialization now let's try this again and you can see when i fire the next projectile in the pool is enabled and used but when it despawns it gets destroyed so we need to change the despawn behavior in our projectile script instead of destroying the projectile we just need to disable it so it can be used again now let's go look at what happens you can see when i fire a projectile after a couple of seconds it gets disabled and when we get back to the beginning of the list we reuse it but now there's some other issues the projectiles are starting to move at different speeds and fly all over the place and if i let them sit they aren't despawning anymore there's a few reasons that this is happening so let's go through them one at a time first let's go look at what happens if an object is being spawned again before it's able to despawn this happens if we're firing projectiles too fast and they don't have a chance to despawn yet there's a few ways you can handle this and one of the simplest is to just forget about the old version and respawn it as a new projectile this is what we're already doing but there's something we need to add to make it work better you always need to make sure you disable and re-enable the object this makes sure that the object gets put back into its initial state as long as you initialize it properly and we're about to see that the rest of the problems come from not initializing the projectiles right now if we run the game again we see that we still have a lot of the same problems so far but disabling the objects before they get reused is the first step to solving all of these other issues so let's first look at why projectiles won't despawn if we look at the projectile script we can see that on start we start the code routine that will despawn the projectile the problem is that the start method is only called when an object is created not when it's enabled to fix this we can just change start to on enable the on enable method is where you're going to want to put all initialization for the object you're trying to spawn i usually like to add an initialization method to the object if there's any things that need to run every time it's enabled this just makes it a little easier to make sure you initialize everything now the projectile should always despawn after they get enabled but they're still flying all over the place and they change speed after we reuse them this is because they're keeping their velocity that they had when they despawn and then we add a new force to them when they respawn now we have a choice to make we could either reset the velocity to zero in our initialize method when the object gets enabled or we could reset it to zero when the object is disabled since we add a force to the projectile as soon as it spawns it makes more sense to me to reset on disable this makes sure that we don't reset the velocity immediately after we add a force to it let's try setting the velocity to zero when the projectile despawns this looks like it's working but if we spawn too many projectiles we see that we still have the same issue this is because when we reuse the projectile that's still enabled we aren't calling the dspawn method we're just disabling it we could change the object pool script to call despawn instead of disabling the object but then we'd have to make an interface for all the objects that we want to use in an object pool instead of that let's just move the reset to the on disable method this way the object gets reset every time it's disabled now we can spawn in projectiles with no issues when you're using object pooling make sure you get the object initialization and reset right because getting it wrong can cause some bugs that are really difficult to find instead of reusing the oldest object you could also just skip spawning a new one you wouldn't want to do this for something like a projectile but it's a good solution if the player wouldn't notice if something new didn't spawn but it would be obvious if an object that already existed were to despawn think about playing something like minecraft you don't notice all the mobs that don't get spawned but it would be pretty strange if a creeper vanished right before it blew up a more complex solution to running out of objects in the pool is to just add more objects dynamically basically if there's none available just create a new one it's not that difficult to implement but it starts to eat into your performance savings if you're just going to be instantiating new objects anyways to demonstrate this instead of resetting an existing object we can use the instantiation from our initialize method to just create a new one and add it to our pool we also have to make sure that we set the temporary variable reference to this new object let's look at how that works you can see that when i use all of the objects in the pool it starts adding new ones and if i keep firing some of the older projectiles will start to despawn but it still creates new ones if you want to fix that you just need to skip over incrementing the index whenever you create a new object in the pool i'm going to switch it back to just reusing the oldest objects and show you that sometimes the best solution is to just increase the initial size of the object pool just remember each time you do this it increases the amount of memory that you're using for all of this i've been making the assumption that the oldest object in the pool is always going to be the first to despawn which isn't always the case but making sure you use all of the available objects adds more complexity and isn't always worth it one other addition we can make to the object pool is to add a field for a container the container becomes the parent for all of the objects in the pool and it's a good place to hold information that each of the objects will need to be able to access for example if all the objects needed a reference to the player instead of finding that reference for each of the objects you can keep the reference in the container and then each of the objects can access it from there to set the parent for each of the objects we can just add a game object into the instantiate method now i can create an empty game object to act as the container for the projectiles and when i start the game all of the projectiles will be children of this container this object pool is working great but what if we wanted different types of projectiles or we wanted to create object pools for all the different enemies we could keep adding more object pools where we need them but a cleaner solution is to have a central location for all of them let's create another script called object pooler this will act like a container and interface for all of the object pools we create first let's add a list of object pools and make it a serialized field so we can create new pools from the inspector i also like to have a way to identify each object pool so let's go back into our object pool class and add a new field for a name we could just use an index for each pool but then you have to remember the index for the object you want and the indexes could easily get changed by accident which could cause a lot of issues if you wanted to i'm sure you could come up with another way to give an identifier to the object pools but this was the simplest way i thought of we'll have to initialize all of the pools so in the awake method of the object pooler we can loop through the list of object pools and initialize each one now we can add in the spawn method again we want to return the game object so we can use it but now we need to pass in the name for the pool that we want to spawn an object from then we can loop through all of the pools to find the one with the same name and spawn an object from it at this point i decided to change the pool name variable because it sounded redundant now if we don't find a pool with the name we passed to this method let's add a warning to help debug later we'll also return null since we were unable to get an object to return now let's create an empty game object and add the object pooler script to it since the object pools are serializable to add a new one we just have to go to the object pooler and hit the plus button then fill out all this information now in the playercontroller script instead of having an object pool we can get a reference to the object pooler and spawn objects through that and here's where i learned why you don't want a container for the objects to be a child of something that moves unless of course you want them to follow the movement of that parent but that's not usually how a projectile works so let's fix that really quick if you don't want to have to pass a reference to the object pooler everywhere you can make it into a singleton i typically don't like to use singletons because they can cause some annoying problems but in this case the object pooler is a pretty good candidate for it now in the player controller we can get rid of this object pooler and just reference the current instance if you want to spawn multiple objects at once we can recycle most of the spawn method and add another parameter to specify how many items we want to spawn you could just call the spawn method multiple times but because of this for loop and the string comparison it's more efficient to make a new method that will only run those parts once in this case we want to return a list of all the objects that get spawned with that you should be able to use object pooling whenever you need to so as always thanks for watching make sure to like and subscribe and i'll see you next time
Info
Channel: Soda Rocket Studios
Views: 447
Rating: undefined out of 5
Keywords: Unity, Game Dev, Indie Game dev, indie games, object pooling unity, unity tutorial, object pooling, design pattern, c# tutorial, c#, programming tutorial
Id: 1WxGmYgO6yE
Channel Id: undefined
Length: 21min 25sec (1285 seconds)
Published: Fri May 21 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.