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

Official Generic Object Pool

Discussion in 'Open Projects' started by davejrodriguez, Oct 19, 2020.

  1. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    I propose generic object pooling functionality. Object pooling aims to alleviate the performance hit of instantiating/destroying many objects by activating/deactivating the objects instead, reusing objects when needed. Several systems could benefit from object pooling: Enemy, projectile, and item spawns, on-demand audio, and on-demand effects.

    If a generic object pool were implemented, these systems could have consistent object pooling functionality and would not have to implement system-specific pools.

    If this were to become a desired feature, I would like to work on it and would implement something along these lines: https://liamederzeel.com/a-generic-object-pool-for-unity3d/

    I would like to hear from other collaborators about whether generic object pool functionality is desirable and what they would like to see in one from the perspective of systems they're envisioning.
     
    Last edited: Oct 19, 2020
  2. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    87
    Hey, I think this would be useful! A lot of systems use pooling so having a generic solution we can just plug in would be really convenient.
     
  3. jamespaterson

    jamespaterson

    Joined:
    Jun 19, 2018
    Posts:
    391
    It's a nice idea. The biggest problem in practice i would expect is how to achieve resetting of complex object state in a generic way. Having implemented an object pool for my own game this was the biggest challenge and actually for some classes of object e.g. compound physics objects proved impossible AFAIK.
     
    Eluem likes this.
  4. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    @jamespaterson Do you mean triggering a reset on a pooled object from a generic pool? Or specifically resetting the complex data you were working with? Because if it's the former, you simply restrict the poolable types to those that implement an IPoolable.Reset. If it's the latter, do you have a repo or example I could look at to see?
     
  5. jamespaterson

    jamespaterson

    Joined:
    Jun 19, 2018
    Posts:
    391
    Hi. Really i refer to the difficulties in resetting some forms of gameobjects, particularly those involving the physics engine. I am afraid i don't have a specific example to hand but here are some examples of things very difficult to reset to an initial condition without destroying and reinstatiating from prefab:

    1) multipart objects e.g. an object which fractures (achievable)
    2) objects with configurable physics joints (achievable with some weird hacks)
    3) an object like (1) where the sub parts have rigidbodies (impossible afaik)

    In addition there are challenges around assuming a single atomic reset call can be carried out in a single frame, e.g particle systems which must "die out" and then be returned to pool (not impossible but implies a kind of 'deferred' reset action)

    Obviously for (1) to (3) the objects must have been used, e.g. split apart / had forces applied. I suppose really this is an issue with an inability to zero the physics engine more than anything else. But there may well be other examples where resetting to initial state is difficult.
     
  6. jamespaterson

    jamespaterson

    Joined:
    Jun 19, 2018
    Posts:
    391
    Here's another tricky scenario. Spawning pooled objects on other pooled objects. Imagine you have a pooled enemy in game. A projectile hits the enemy, so you spawn a pooled damage effect e.g. particle system inside the enemies hierarchy to allow it to move with the enemy. Now the enemy dies and is reset. The pooled damage effect needs to be detached and returned to the pool seperately otherwise next time to enemy is used from the pool it will already have the damage effect on it.
     
    DotusX likes this.
  7. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    I think having such a system is a good idea, however, I don't think we need to complicate it too much.
    In this game, I can see it being used for AudioSources and Particle effects for sure.

    @jamespaterson your example of potential issues are great. We can use them to try and navigate around them. For instance: in the case of particles, it would be good to think of setting the pooled object only if the particle system reached its full playback (?)

    So for instance, in this case it would be responsibility of the pool to track and check if all of its particle systems have finished playback. This is arguably "lighter" than having a MonoBehaviour on each particle system that checks itself.

    Can we think of these specialised pools?

    Also, @davejrodriguez, I like that tutorial you posted as a base to start with. We can start with something simple and build on it as we go. If anyone else wants to participate, feel free to collab in one PR?
     
    jamespaterson likes this.
  8. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Specialized pools was exactly the sort of thing I was hoping to avoid by proposing generic pools. I think the logic for how a poolable object resets should live on the poolable object. Then the pool only has to do one thing - be a pool. I see it as a sort of collection like list. You wouldn't bake logic into List<T> that says when or how T should be added/removed.

    However, I agree the issues @jamespaterson listed are things we definitely want to avoid. I've been prototyping some things and I'll certainly be sure to test against these scenarios. Perhaps it will be impossible to keep it generic, but I'd like to give it a try.
     
  9. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    Yes, how the object resets... but when is the object available, I think that could totally be in the hands of the pool.

    Why are you against specialised pools? They could be inheriting from a base type, and implement their own "CheckIfAvailable".

    Anyway, I'll leave it to you!
     
    davejrodriguez likes this.
  10. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Ah, ok. Yes I agree, absolutely the pool should determine object availability.

    Oops, my mistake. For some reason, "specialized pools" triggered some idea of a different architecture not using a generic base. I am not against specialized pools. That sounds like a excellent solution.
     
  11. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Hello all,

    I've started a draft PR here: https://github.com/UnityTechnologies/open-project-1/pull/117

    Please tear it apart :)

    Note: I did not use an override-able CheckIfAvailable method in this first iteration, because the reset callback I implemented to address the "reset state over time" goal kind of naturally lets the pool know when an object is available. If we don't like the callback, I'm more than open to going down that road.
     
    Last edited: Oct 28, 2020
    deivsky likes this.
  12. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Made some changes to the PR that @deivsky suggested. Mostly a paring down of functionality. Pool no longer maintains a reference to unavailable objects, which both simplifies the class a bit and ensures that we don't maintain a reference to an improperly destroyed object, hindering GC. ComponentFactory was pared down considerably and now does the bare minimum of what it needs to do.

    Some new features include batch Request and Return for Pool.

    I've also been experimenting with ScriptableObject versions of the Pool and Factory. Factory works no problem. Pool has proved a little more complex, but promising.

    Please take a look and test it out if this stuff interests you!
    https://github.com/UnityTechnologies/open-project-1/pull/117

    EDIT: Added experimental scriptable objects as well. I hope to make some progress on them this weekend.
     
    Last edited: Oct 31, 2020
  13. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    Looks good, just please keep it simple for the first iteration. The readme you provided in the PR is great though.

    We should start thinking of this kind of mini-manuals for all systems, so people know how to put them to use! Where should they go?! A wiki on the repo?
     
    deivsky and davejrodriguez like this.
  14. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Yes, aiming for simple. I promise! Not planning new functionality. Just rethinking how you create/use pools. I think being able to use pools/factories as scriptable object assets is in line with the videos linked in the Conventions doc and will be better for systems in the long run.

    After removing the example, this PR should only be 7 files and 188 lines of new code.

    As for documentation, I was actually thinking of including a local README in the folder, but a repo wiki would be even better!
     
  15. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    87
    I was checking the implementation of the pooling system in the Audio System by elocutura and the problems they had with it, and it made me think IPoolable is doing more harm than good.

    The pool doesn't need to know whether something needs to do anything when requested or returned, so there's no need to couple them together. As far as the pool's concerned, we could have a pool of integers with a factory that simply calls Random.Next(). If an object needs to be reset, it can implement some `IResettable` interface and we can leave the responsibility of resetting it to the pool owner, instead of the pool. Same thing goes with initialization after requesting the object from the pool.

    Maybe it isn't more foolproof, because now you have to implement extra steps after requesting and before returning objects to the pool, but I think it's a cleaner design. Like, an object shouldn't need to be explicitly poolable just so it can be pooled, just as it doesn't need to be IListable to be in any IList. What do you think?
     
    davejrodriguez likes this.
  16. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    I'll need to look into it some more once I have some time, but I'm starting to get that feeling. Would remove the need to wrap Unity components and built in types as well.

    On a semi-related note, I'm also finding while writing documentation that ComponentFactorySO doesn't bring much to the table while assuming/enforcing a prefab. You could get the same functionality from FactorySO without enforcing prefab and just override to Instantiate. Considering removing.

    Semi-related because IPoolable and ComponentFactory are kind of these remnants from the tutorial I started from. We've kind of moved away from that implementation. So maybe it's time to rethink them.
     
  17. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,020
    I was going to post some thoughts about it so finally I decided to show you how I do it.

    All prefabs that are clients of pooling must have component derived from IPoolClientSiteComponent in its root transform. This component responsibility is, being a site for IPoolClientComponent , Releasing (object to the pool client) , and Reseting object (refreshing when retrieved from pool, objects in pool are dirty when waiting on a pool).

    Usually component deriving from IPoolClientSiteComponent should destroy object instead of returning it to the pool when it not poses valid PoolClient (inside) this lets you use object even without pooling.
    The only thing that needs to be remembered is that you do not destroy object by using Object.Destroy but by calling GameObject.Get<IPoolClientSiteComponent>().Release().

    Now IPoolClientComponent is a connection between IPoolClientSiteComponent and IPoolComponent.
    It got only two members Release and ResetObject.

    Lastly IPoolComponent this component is responsible for actually managing the pool.
    It holds pooled objects and allows you to retrieve them. It got two members.

    Code (csharp):
    1.  
    2. public bool TryAcquireObject(GameObject prefab, out GameObject go)
    3. public void RegisterObject(GameObject prefab, GameObject go)
    4.  
    So before instantiating object you "TryAcquireObject"
    if it failed you instantiate object and call RegisterObject.
    Argument "prefab" of this methods let's you identify what kind of object we want to obtain, so we can have multiple types of pooled object on one pool.

    So to finish it with a short exemplar story:

    - We want to spawn Bullets from our Rifle.

    - Create our concrete PoolComponent on our Rifle. When object is pooled it will disable the object and parent it, so when we destroy pool all pooled objects are going to be destroyed with it. (but it also might be global pool shared between rifles)

    - When firing Rifle first check if it can acquire Bullet from pool if not it will PoolComponent.RegisterObject.

    During registeration IPoolComponent adds it's dedicated concrete IPoolClientComponent to Bullet. ()
    Than search for IPoolClientSiteComponent and do IPoolClientSiteComponent.SetPoolClient(IPoolClientComponent).

    If object was Acquired from pool than registration was already made and we only have to place object when we want and call GameObject.SetActive(true). We do not worry about reseting object, PoolComponent should call IPoolClientComponent.ResetObject (during acquiring) and this should call IPoolClientSiteComponent.ResetObject this is why IPoolClientComponent should know what IPoolClientSiteComponent it is connected to.


    Code (CSharp):
    1. public abstract class IPoolClientSiteComponent : MonoBehaviour{
    2.  
    3.     public void SetPoolClient(IPoolClientComponent poolClient) {
    4.         OnSetPoolClient(poolClient);
    5.     }
    6.  
    7.     protected abstract void OnSetPoolClient(IPoolClientComponent poolClient);
    8.  
    9.  
    10.     public IPoolClientComponent GetPoolClient() {
    11.         return OnGetPoolClient();
    12.     }
    13.  
    14.     protected abstract IPoolClientComponent OnGetPoolClient();
    15.  
    16.  
    17.     public void ResetObject() {
    18.         OnResetObject();  
    19.     }
    20.  
    21.     protected abstract void OnResetObject();
    22.  
    23.  
    24.  
    25.     public void Release() {
    26.         OnRelease();
    27.     }
    28.  
    29.     protected abstract void OnRelease();
    30.  
    31.  
    32. }
    Code (CSharp):
    1. public abstract class IPoolClientComponent : MonoBehaviour{
    2.  
    3.     public void ResetObject() {
    4.         OnResetObject();
    5.     }
    6.  
    7.     protected abstract void OnResetObject();
    8.  
    9.  
    10.     public void Release() {
    11.         OnRelease();
    12.     }
    13.  
    14.     protected abstract void OnRelease();
    15.  
    16. }
    Code (CSharp):
    1. public abstract class IPoolComponent : MonoBehaviour
    2. {
    3.  
    4.     public bool TryAcquireObject(GameObject prefab, out GameObject go) {
    5.         return OnTryAcquireObject(prefab, out go);
    6.     }
    7.  
    8.     protected abstract bool OnTryAcquireObject(GameObject prefab, out GameObject go);
    9.  
    10.  
    11.  
    12.     public void RegisterObject(GameObject prefab, GameObject go) {
    13.         OnRegisterObject(prefab, go);
    14.     }
    15.  
    16.     protected abstract void OnRegisterObject(GameObject prefab, GameObject go);
    17.  
    18. }
    I know it sounds quite convoluted and might be hard to swallow but it is quite flexible system.
    As you can see I have not posted concrete implementations I don't want to make this post even longer.
     
  18. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    @deivsky Alright, I've submitted a PR to remove IPoolable and ComponentFactorySO: https://github.com/UnityTechnologies/open-project-1/pull/159

    Hey @koirat, glad to see other ideas in this thread. I am having a bit of a struggle figuring out what that code is doing though. I think it might be helpful to post a concrete implementation if you've got one (a gist or a branch on github if it's too long to post here?).

    I have a few questions:
    1. Why are you using interface naming convention for abstract classes?
    2. What would the advantage be to having dedicated pool components over the current scriptable object variant?
    3. Why does there need to be a IPoolClientComponent layer between IPoolClientSiteComponent and IPoolComponent? (this might be answered by a concrete implementation)
    4. W̶h̶a̶t̶ ̶w̶o̶u̶l̶d̶ ̶t̶h̶e̶ ̶a̶d̶v̶a̶n̶t̶a̶g̶e̶ ̶b̶e̶ ̶t̶o̶ ̶t̶h̶i̶s̶ ̶o̶p̶t̶i̶o̶n̶ ̶o̶v̶e̶r̶ ̶t̶h̶e̶ ̶c̶u̶r̶r̶e̶n̶t̶ ̶i̶m̶p̶l̶e̶m̶e̶n̶t̶a̶t̶i̶o̶n̶?̶ ̶A̶l̶w̶a̶y̶s̶ ̶o̶p̶e̶n̶ ̶t̶o̶ ̶n̶e̶w̶ ̶i̶d̶e̶a̶s̶,̶ ̶b̶u̶t̶ ̶n̶o̶t̶ ̶y̶e̶t̶ ̶s̶e̶e̶i̶n̶g̶ ̶s̶o̶m̶e̶t̶h̶i̶n̶g̶ ̶h̶e̶r̶e̶ ̶t̶h̶a̶t̶ ̶t̶h̶e̶ ̶c̶u̶r̶r̶e̶n̶t̶ ̶i̶m̶p̶l̶e̶m̶e̶n̶t̶a̶t̶i̶o̶n̶ ̶d̶o̶e̶s̶n̶'̶t̶ ̶d̶o̶.̶ Ah I think I see now. Looks like you trade having to write Factory/Pool SOs for each new type for having to GetComponent to get the target type.
     
  19. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,020
    1. I know it breaches a little the c# convention but since I cannot use interface for component I'm simulating it with abstract class derived from MonoBehaviour. I could use some different prefix but I decided to use "I" in the end. When I see I*****Component than I'm 100% sure it is an abstract class and the most generic one for particular set of types.

    2. Dedicated pool component might implement almost any kind of pooling system. You may create pool component that will use ScriptablerObject pooling for example. It is a matter of flexibility.
    Now when you got PoolComponent you also get events like OnDestory.

    It is also more convenient for my own system where I use special InstantiatorComponent instead of raw GameObject.Instantiate. This instantiators can modify prefab (creating intermediate prefab) before final instantiation so I cannot have one shared pool for one type of prefab.

    3. Theoretically we could do it without it. And just connect IPoolCompoent with IPoolClientSiteComponent directly but making it current way gives us some advantage. First of all "contract" IPoolClientSiteComponent does not need to have direct access to IPoolComponent. Also when we add IPoolClientComponent to created object we get access to events like OnDestroy etc..

    4. Yes I'm not writing any specified object for types, my system can swallow any prefab. You can have random list of prefabs and use the same PoolComponent for all of them.
     
  20. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    @davejrodriguez I might be misunderstanding something, but why the method
    InitializePool
    is private?
    I see it's called by the pool itself the first time a
    Request
    is called. But this defeats the purpose of the pool: now in the middle of the game I want an object from the pool, and instead of instantiating one I am instantiating the whole size of the pool in one frame.

    Logically I'd expect that whoever is responsible for the pool (in my case, the AudioManager) pre-warms the pool by calling
    InitializePool
    at the beginning of the game/scene, etc.
     
  21. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    I was thinking more about decentralized, lazily-loaded use when I did that. But yeah, I definitely see how it could lead to spikes in the middle of gameplay. I agree, public prewarm functionality is necessary.

    I've put together a PR here: https://github.com/UnityTechnologies/open-project-1/pull/202

    Requests (and returns) can still happen without calling Prewarm first, but now it will only expand by the number requested instead of the (now removed) InitialPoolSize.
     
    cirocontinisio likes this.
  22. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    Great, saw the PR!

    Yeah, the reality is that with pools - especially for this little game - I can rarely see the use of decentralised ones. It's much more possible we will have one pool for any type of object: one for SoundEmitters, one for Hit particles, one for X type particles, and so on.
     
  23. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    I'm a little confused by this statement. It's fine if we prefer using centralized managers. But when I say decentralized, I mean decentralized access to one pool, not multiple pools.

    PoolTester.cs + SharedGlobalParticlePool.asset in the Example scene demonstrates this decentralized access. Multiple instances of PoolTester access the same pool without some centralized "PoolTesterManager" coordinating things.

     
    Last edited: Nov 20, 2020
    cirocontinisio likes this.
  24. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    Ah, I see what you mean. Yeah, I thought you meant that there was more than one pool of the same type of objects.

    In any case, the problem remains: the number one advantage of pooling is to be able to pre-spawn the objects, so you don't pay the Instantiation costs during the gameplay (which in Unity are notoriously high). So lazy initialisation is not an option.
     
  25. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    I merged the Prewarm PR.

    A couple more things on the Object pooling:
    • I am a bit confused now as to why we have SOs for the Pool and for the Factory. Looks like a very convoluted workflow to have two SOs and have to worry about connecting them to a script (case in point, the AudioManager) when all you really want to host on the manager is: what type, and how many objects to pre-spawn.
    • DontDestroyOnLoad
      . I don't think we should be using it. This means that if we move from scene to scene (e.g. from gameplay back to menu) now we have a bunch of pools in memory for no reason. I think objects that request pools should keep the pool with themselves. In fact, they could be parented to them? This way if the object goes, the pool goes. I feel like it's a cleaner pattern.
    All of the above is just thoughts, as always! Don't take them as requests, and I might be horribly wrong :)
     
  26. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Alright, long post, here we go:

    Why SO pools?

    1. Well, because you can create them as assets and set them as references in the inspector. This allows for decentralized access, which would be impossible with only runtime pool creation. (I know you're in the centralized mindset, but please keep reading :))
    2. Since the asset exists in the project, these references can be prefabbed, which means that instantiated objects do not need to FindObjectOfType<PoolManager> or PoolManager.Instance to call a method which will request an object from a pool on the manager. They can simply ask their pool reference for an object.

    Why SO factories?

    1. Like I've said before, pools and factories are two distinct concepts that can be used independently. Other systems may need factories and, for the reasons above, I think SO factories are a great solution.
    2. Modularity. For example, you can write AudioSourceFactorySO that takes a AudioSource prefab and a bool `looping` in the inspector and instantiates it with `loop`=`looping` as the default when Create is called. Create two instances as assets in the project "OneShotFactory" with `looping` set to false and "LoopFactory" with `looping` set to true. You can swap the factories at will at compile time or runtime and it doesn't make a difference to the pool, because it's still serving AudioSources. Sure you could make two prefabs for this, but if factory SOs are an option, might as well use it. And if we expand beyond Components for a second and consider an Pool that serves interfaces or base classes, modular factories become even more useful.

    Why DontDestroyOnLoad?

    I think you've got a point here. When I added that, my thinking was in the context of audio sources and wanting them to continue playing through scene changes if necessary. And also thinking, "what game scene wouldn't need pooled audio?" But it is not good that pools should persist into scenes where they might not be used. However, DontDestroyOnLoad can be very useful in some pool contexts, especially in decentralized ones. I'm thinking maybe I can make it optional? Maybe allow PoolRootObject to be set externally? A Trim() function to remove unused objects from pools? I'll experiment a little bit to see if I can find a good compromise that makes sense.

    On AudioManager

    I'm glad you mentioned AudioManager, because:

    ive-got-a-2c60d24a7e.png

    Well, mostly one problem. I cannot figure out what AudioManager is bringing to the table in terms of pool management. Sound requesters have to 1. FindObjectOfType<AudioManager>() 2. Call PlayAudioCue(...) and they're not even rewarded with access to the SoundEmitter. If AudioManager were doing some processing that involved knowing about more than one SoundEmitter, it might make sense that it's a go-between. But right now it just seems like AudioManager is getting in the way of actually using the pool.

    For example, how do we request sounds that follow the requester? Not possible as it stands. So add another parameter to the PlaySoundCue method to take in the requester's transform to parent the SoundEmitter? Sure, but why not just give the SoundEmitter to the requester? I asked this once in my PR 90 review and the only response was that it didn't fit the brief.

    In my opinion, a simpler and better solution would be to have a reference to a common SoundEmitter pool on every requester (can be prefabbed). Requester requests a SoundEmitter. They get a SoundEmitter. They do whatever with it, parent it, play it however many times, loop it, toss it up in the air. Then return it to the pool if they're done with it or get destroyed or whatever. As far as the requester is concerned, they just instantiated/destroyed a SoundEmitter prefab with different words (request/return). No need to know about this centralized AudioManager who for some reason holds the keys to their audio dreams.

    But that's just my thoughts on it.
     
  27. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    Agree with this.

    Hmm, ok. I see the point of modularity but I don't see a use case for it yet. In the example you make, the
    isLooping
    parameter might as well be on the AudioSource itself or on a script that it has on. But by putting it in the factory, I guess you avoid having it on the objects, so it's more centralised. Fine. I don't want to remove factories SOs, I'm just trying to understand use cases.

    Yes that's my thinking too, but since the AudioManager lives in a scene which never gets unloaded (Initialization), then the pool never dies and audio doesn't stop playing (unless the AudioManager specifically stops it, which is the case for SFX).

    For other pools though, like particle effects, it makes more sense that they get removed when the scene is unloaded!

    Hahahahahahaha :D

    I am doing a lot of work on the AudioManager, and a lot of thinking. Let's bring the conversation to the appropriate thread!
     
    Last edited: Nov 22, 2020
    davejrodriguez likes this.
  28. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Alright, new PR up now: https://github.com/UnityTechnologies/open-project-1/pull/215

    Please see LocalPoolTester.cs for implementation changes.
    • DontDestroyOnLoad has been removed from ComponentPoolSO. Pools now do not survive scene changes by default.
    • Added SetParent to ComponentPoolSO.
    Decided against adding an explicit option for DDOL as I think SetParent can be used to achieve that and more. Also, did not add Trim functionality for now as that would require some sort of destruction logic, like an inverse factory. We can add later if we find it's necessary.
     
    cirocontinisio likes this.
  29. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    884
    The PR is good.

    But I'm so confused about one thing.
    HasBeenPrewarmed
    (in the
    PoolSO
    class) is verified in the
    Prewarm
    method and... always returns true? So the pools are never really prewarmed, but they add SoundEmitters as they are requested. Why?
     
  30. davejrodriguez

    davejrodriguez

    Joined:
    Feb 5, 2013
    Posts:
    69
    Sorry, holiday weekend and I did not have access to a dev machine.

    Hmm, if
    HasBeenPrewarmed
    of a pool asset is always true, it may be carrying over across play sessions. The fix would be to reset the boolean to false in OnDisable of
    PoolSO
    . I'll check it out during my lunch break to see if that's the case.

    EDIT: Yes, this was the issue. Made an issue and PR with the fix.
     
    Last edited: Nov 30, 2020
    cirocontinisio likes this.
  31. ssmas

    ssmas

    Joined:
    Jan 3, 2019
    Posts:
    2
  32. MidniteOil

    MidniteOil

    Joined:
    Sep 25, 2019
    Posts:
    342