Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Am I relying too much on Instantiate, and is it such a bad thing?

Discussion in 'Scripting' started by PaperMouseGames, Nov 5, 2019.

  1. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Hi there! So I'm working on my first real big project and I've heard many times that Instantiating and Destroying game objects is not a good idea in Unity. I've been told to look into object pooling, which I think I understand the basics of. I think the idea is that you keep objects around that you enable and disable based on your needs rather than Instantiate them, from say a prefab, and then destroy them.

    The issue is that I'm just really not sure how to handle certain situations with this and so I've continued to use Instantiate and Destroy. Right now I'm using Instantiate and Destroy several things like spawning enemies and gatherables, as well as items that get dropped or placed in the world.

    For example, I'm working on an RPG and I just started trying out some projectiles. So I'm Instantiating arrows and fireballs, and then I'm destroying them when they collide with something.

    Another example is dropping items in the world (think Skyrim cheese wheels). Would I need to have a bunch of dropable items just sitting somewhere and then activate them when I need them to be dropped?

    Is it really so important to switch to object pooling for this stuff? How could I go about it?

    In the projectile example, would I need to determine how many projectiles the player is allowed to launch at once?

    Anyway, some feedback on this would be really helpful, if object pooling is a must, I'd love to hear some strategies for dealing with some of my examples.
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,594
    Pooling is simply a collection of objects, which you can reuse, during game play and which is predefined at initialization. Alternatively, such collection can grow if needed, to accommodate more objects.

    So if you have array or list of bullets, you simply reuse them in scene.
    You need track, which bullet is in play, which is a spare.
    You can use simply offset index, to indicate, which objects are spare.
    When bullet hits target, id is put back to collection. For example, assigning as next index in the array, increasing offset index.

    Same applies to anything else, that can be reusable.

    You either create enough size for each collection, if you know no more that set number of items is going to be used.
    Or set some minimum valid value, and expand collection at run time when needed.

    Other than that, I suggest to check internet for different implementations.
     
    PaperMouseGames likes this.
  3. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    It depends on your project - how repetitive your objects are, the hardware you intend to run it on, how tight your development schedule is. That said, unless you're at the extremes of all of those, the answer is almost always "more pooling". Object pooling costs basically nothing in terms of performance, and only a little in terms of game development effort. If you're not competing in a 24 hour hackathon or something, there's little reason not to do it.

    And if you don't know how to do it, it's a valuable tool that you don't have in your toolbox. If your toolbox doesn't have a hammer, you could flip your screwdriver around to bang the nails in, but it's clear that if your project involves nails you really should just get a hammer.

    The easiest way to go about it: Find a package on the Asset Store that handles it for you. There are tons, and I think some of them are even free.

    That said, it's a really simple concept and worth learning how to implement yourself, so just googling "object pooling tutorial" should give you a lot of good, usable results.
     
    PaperMouseGames likes this.
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    There's two possible answers:
    1) No, just choose a sensible maximum. Even if it's higher than necessary, that's still preferable to lots of instantiation.
    2) You can write the object pool in such a way that, if you try to spawn an object and you're out of that kind in the pool, it instantiates a new one, and adds it to the pool. In fact, a lot of implementations of object pooling only instantiate new objects on demand, and then when those eventually get destroyed, hey, there's your pool.
     
    PaperMouseGames likes this.
  5. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Thanks for the replies! Looks like I'll be learning more about object pooling methods then.

    Your second point is interesting, so are you implying that the Instantiate method isn't so bad when it comes to memory / long term performance, but the real trouble is with the Destroy method?

    Otherwise, if you're using the Instantiate method for object pooling it would seem like it's missing the point.
     
  6. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,594
    Memory is not your issue. The CPU usage, which requires creating and destroying new objects and related data.
    Plus, you have whole garbage collector following the process.

    So potentially ramping down your FPS. Or increasing unnecessarily battery usage, if talking about mobile devices.
     
    PaperMouseGames likes this.
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,748
    No, the point is that an object only gets instantiated once. "The real trouble is with the Destroy method" is kind of true in a roundabout way, in that you're destroying something that you could be reusing. (Sort of like - to go on a bit of a tangent - when you hit the brakes on the highway, you're reducing your gas mileage. The brake pedal obviously doesn't burn any gas, but it does destroy momentum that you will later have to build back up by burning gas. This is the same sort of thing.)

    You're going to be instantiating objects no matter what (even with "everything happen up front" style object pooling, you're still instantiating them, you're just doing it all in Start(). The idea behind the "on-demand object pooling" concept is 1) you don't have to think about how many objects you might need, and 2) slightly faster startup times. During the first "buildup" of objects, yes, it'll still take the time to instantiate them and that period of gameplay will be unchanged from not using object pooling at all. But in almost all games that "buildup" is a small fraction of the total gameplay time.
     
    PaperMouseGames likes this.
  8. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,926
    If you keep looking, you can find a list of things you "should" be doing as long as your arm. Some are wrong, some are only useful in very specific situations, some are very easy to mess-up. You should plan Achievements from the start (actual advice I've heard, and probably good).

    An object pool makes instantiating more complicated, adds new potential bugs (not resetting everything from a re-used object) while possibly giving a small speed-up. Are you having trouble with sluggish scenes during Instantiates? Do you want to stop working on your game for a week or two? Is your game even finalized enough to worry about it?
     
    Joe-Censored and PaperMouseGames like this.
  9. flipwon

    flipwon

    Joined:
    Dec 29, 2016
    Posts:
    179
    I mean, you can literally setup a working object pool in like 10 lines of code, so I'm not sure about this whole complexity thing.. I'd say in the case your game has a lot of bullet firing etc it's pretty much always worth it to look into pooling, if only for the bullets.
     
  10. Nadinya

    Nadinya

    Joined:
    Mar 1, 2019
    Posts:
    5
    Object pooling is something you do need to know. Google around more then enough tutorials on the subject
     
  11. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,926
    It doesn't have a lot of bullets. The is OP is writing about enemies, gatherables, and only arrows and fireballs. This is a big deal. If they wrote "my frame rate drops when I fire a 50-bullet blast" it would be one thing. Massive bullet counts are the simplest and best use of recycling. But this is "some rando told me to pool power-ups, should I?"

    As for time, it takes at least hours to figure out how to use a system, and find one that works. Then writing code to create an object pool for each type of object. Some need extra code to reset particle systems (and child systems, Arg!) Any scripts using Awake or Start need rewriting and a custom pool. Any that change during use need extra code to reset that. The mere fact of changing how things work will add bugs. Then when any of that changes, the pool system needs to be changed to account for it.

    Sure, put "learn how recycling objects works" on the list. It's a real thing. But rank it behind "get better at making good particles", "learn more about lighting", "learn quaternions", "find an inventory system" and so on.
     
    PaperMouseGames likes this.
  12. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Like Owen says, aim at the low hanging fruit. Bullets, particle systems etc. Things like that are early candidates for pooling. Complex domain logic can be a bitch to reset. For example we are making a advanced VR shooter simulation and the weapons are super complex state machines. We choose to create and destroy those. There aren't that many weapons in the scene at any given time anyway
     
    PaperMouseGames likes this.
  13. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Once again, thanks to everyone who has replied to this! I've done a little bit more research into object pooling now and though I'm unsure I'm gonna start implementing it right away, it's definitely on my list of things to learn.
     
  14. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    To quote something I posted in a similar thread:
     
  15. jamespaterson

    jamespaterson

    Joined:
    Jun 19, 2018
    Posts:
    391
    Object pooling offers greater efficiency at the cost of complexity. The reason this complexity comes about in my personal experience is due to needing to rewind any changes to state when returning to the pool. Consider you have a object with a rigidbody. Once forces get applied then the object has velocity. You have to be sure to remove this before reusing the object. Consider a composite object which when exploded falls apart. Now you need to reassemble that object before it can be reused. But what if the parts are available to return to the pool to reassemble the object at different times? What about if the game object has a particle effect which needs to complete before returning to the pool?

    With hindsight i probably would have restricted my use of pooling to simple objects, relatively stateless objects of which there are many in use, e.g. bullets / explosions. An alternative is a 'single use pool' Good luck!
     
  16. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Also if you are on 2019 you can look into incremental garbage collection. It offload the GC over several frames.
     
    hippocoder likes this.
  17. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    124
    for simple objects like bullets it's easy if you limit the maximum number of objects.


    Code (CSharp):
    1.    
    2.  
    3.         public int size;
    4. public GameObject go;
    5.         GameObject[] poolObjects;
    6.         int position;
    7.         public void Awake()
    8.         {
    9. this.gameObject.setActive(false);
    10.             position = 0;
    11.             poolObjects = new GameObject[size];
    12.             for (int i = 0; i < size; i++)
    13.             {
    14.                 poolObjects[i] = GameObject.Instantiate(go,this.transform);
    15.             }
    16.         }
    17.         public GameObject getNext()
    18.         {
    19.             position = (position + 1) % size;
    20.             return poolObjects[position];
    21.         }
    22.  
    23.     }
    something like this, however once you exceeded maxsize old objects will autmatically be overwritten which might not be what you want. But it's the most efficient method afaik

    but you should only use it for simple objects which don't effect game-logic, because it can happen that some objects will get overwritten
     
  18. flipwon

    flipwon

    Joined:
    Dec 29, 2016
    Posts:
    179
    There may be a mistake here as I haven't tested.

    Code (CSharp):
    1.  
    2.  
    3. public GameObject PooledObject;
    4. private List<GameObject> _pooledObjectList = new List<GameObject>();
    5.  
    6. //setup pool
    7. void SetupPool(int numToSpawn)
    8. {
    9.     for (int i = 0; i < numToSpawn; i++)
    10.         {
    11.             var obj = Instantiate(PooledObject, gameObject.transform);
    12.  
    13.             obj.SetActive(false);
    14.  
    15.             _pooledObjectList.Add(obj);
    16.         }
    17.  
    18. }
    19.  
    20. //return the first inactive pooled object, if there is none, instantiate a new one
    21. //and set it it active
    22. public GameObject GetPooledObject()
    23. {
    24.     var obj = _pooledObjectList.FirstOrDefault(t => !t.activeInHierarchy);
    25.  
    26.     if (obj == null)
    27.         obj = (GameObject)Instantiate(PooledObject, transform);
    28.  
    29.     obj.SetActive(true);
    30.  
    31.     return obj == null ? null : obj;
    32. }
    just need to set the object to inactive after you're done
     
    Last edited: Nov 7, 2019
  19. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    When you Instantiate in
    GetPooledObject
    , you forgot to add the new instance to the pool.

    There's also no need to check and explicitly return null at the end of the method, since if the object is null, then null will be returned regardless.
    Code (CSharp):
    1. public GameObject GetPooledObject()
    2. {
    3.     var obj = _pooledObjectList.FirstOrDefault(t => !t.activeInHierarchy);
    4.  
    5.     if (obj == null)
    6.     {
    7.         obj = (GameObject)Instantiate(PooledObject, transform);
    8.         _pooledObjectList.Add(obj);
    9.     }
    10.     obj.SetActive(true);
    11.     return obj;
    12. }
     
    flipwon likes this.
  20. flipwon

    flipwon

    Joined:
    Dec 29, 2016
    Posts:
    179
    Ah, there ya are
     
  21. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,926
    That's fun, but I'm guessing there are written and tested objects pooling, with instructions and examples, out there.

    Keying off of active status to know if an item is available doesn't seem safe. The user may want to turn it briefly inactive, then active again, all while using it. Having an "unused pool objects" list is more common, and avoids the loop to find a free one. Why are they your children? Is that so they're all destroyed when you are? It doesn't seem safe, since the user may want to change the parent.

    But it's long been fine to just mess around in a forum thread, as long as you know that's what's going on.
     
  22. flipwon

    flipwon

    Joined:
    Dec 29, 2016
    Posts:
    179
    It's fun, that's object pooling in a nutshell, not sure why it needs to get any more complicated. The parenting and the active status could be easily changed to something as simple as an in use boolean just seemed fast to write up and quick to get the point across. The loop is (O)n, but I mean you could always change it to a stack or something if the size of pool you need is really large.
     
  23. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,926
    I think we're in agreement. If a programmer wanted to mess around, learning the basic idea of object re-use, the code above is an easy-to-understand way that works in common cases.

    My point was, being Forums, someone is going to paste it and say "it didn't work, and had these problems". Well, yeah -- it's not meant for that. Search will find working tested, refined object pool code with examples of use. It may be 100 lines long and use calculus, but who cares, since all you need are the create, spawn and destroy commands for it.