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. Dismiss Notice

Pool Kit - The ultimate system for pooling, spawning and despawning

Discussion in 'Assets and Asset Store' started by melgeorgiou, Jun 20, 2018.

  1. lorewap3

    lorewap3

    Joined:
    Jun 24, 2020
    Posts:
    58
    Hi @melgeorgiou !

    I have a conundrum I'm facing right now with Poolkit that I hope you can help me clarify. I messaged you a month ago and sent you my invoice then but I can again if necessary.

    I'm trying to get an exact handle on spawning script execution. I can't seem to figure out how to reliably manipulate spawned instances before their monobehaviour OnEnable functions get called.

    These are the steps that are happening. All the prefabs are particle systems. Eventually they will be despawned manually by script but for now are despawned by the end of the particle system.

    1) spawner.Play() (Character) - is called at the appropriate time from a ParticleTrigger.cs mono attached to a character.
    2) ParticleColorUpdater.cs (Character) - Is a monobehaviour attached to a character that hooks into OnSpawn events on each of its Spawners (attached to gameobject 'limbs'). This one always seems to execute the OnSpawn callback before the OnEnable() methods are called on the monobehaviours instantiated prefab. It's worked perfectly since last year in the same order every time.
    3) ParticlePayloadSetter.cs (Character)- A monobehaviour attached to the Organism at the spawner level. This one also hooks into the spawner OnSpawn events. Its purpose is to provide a data 'payload' that's used throughout the playtime of the particle system. This one, as opposed to the ParticleColorUpdater, I can't for the life of me get to execute the callback before the OnEnable methods in the instantiated prefab.
    4) ParticlePayloadProvider.cs (Pooled Prefab) - This is a monobehaviour attached to the particle system prefabs as opposed to the characters themselves. This is the key script that has a few fields I need to set using the payload mono mentioned above using the OnSpawn callback. The ParticlePayloadSetter attached to the Character needs to be called before the OnEnable method for this monobehaviour. This is what I can't figure out. This OnEnable always gets called before the ParticlePayloadSetter above.

    (The above ordering is what I want, but what I'm saying is #4 is consistently happening before #3 everytime)

    I wrote the ParticleColorUpdater a year ago and since it's worked perfectly I've never had a need to investigate this. Now that I am investigating I'm learning I have no idea how to ensure the ordering I want.

    Edit: Correction, the ParticleColorUpdater isn't always called beforehand. It just didn't matter, as adjusting colors on a particle system even a frame after isn't visible. The ordering is much more important for the ParticlePayloadSetter, though.

    I tried playing with script execution orders, but that doesn't seem to have any effect. Though, thinking about it, it's unlikely it matters at all as that has nothing to do with events and delegates and just the ordering of different scripts' Unity methods. In this scenario there's no clashing of ordering that I can see. It's all event driven.

    Could you give me some advice Mel? I'm not sure how to troubleshoot something like this. I hope there's some way force it to be consistent.

    Thanks Mel!
    Will
     
    Last edited: Apr 18, 2022
  2. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi Will,

    If I’m not mistaken, I believe OnEnable will probably be called before OnSpawn. OnEnable is likely to be ordered with Unity’s script execution order and PoolKit’s OnSpawn order largely depends on when the GameObjects were created (and the order of your pools). If you want to setup code before OnEnable, the only way to do that is on Awake (when the gameObject is created) or at OnDisable / OnDespawn ( when the GameObject is being turned off so it’s ready for the next spawn ).

    There could be many different and convoluted reasons why this is happening but essentially you have callbacks firing off in an unpredictable order that you’d like to control. I’d recommend a different approach to handling it. :)

    I’d recommend removing the callback code you have running at the moment on the existing scripts. Instead, create a new script called “MySpawnController” for example.

    In that script, cache the necessary components you want to access in its Awake method. Hook up a new OnSpawn or OnEnable on the new script to handle the ENTIRE object.

    Now, when you get the callback from PoolKit for that object, you can fire off whatever methods you want from that one MySpawnController script in the order you want. Also, you optimise your game by only requiring PoolKit to send 1 callback to an object rather than multiple :)

    Hope this helps! :)

    -Mel
     
    Last edited: Apr 19, 2022
  3. lorewap3

    lorewap3

    Joined:
    Jun 24, 2020
    Posts:
    58
    Thanks for the feedback Mel! I had a feeling that maybe the case so I'm glad I asked the expert ;)

    You're absolutely right, I could setup everything in Awake at the beginning, but I was hoping to avoid that. The reason being to keep a separation between the characters and spawned prefabs for maximum reusability. I can definitely do it that way, but then I would guarantee each character would have its own pool that no other character could use.

    Just as an example, let's say I have these:
    1) Character A - Spawns Prefab 'OrbGlow' with a green color and scaled 10x in the X.
    2) Character B - Spawn Prefab 'OrbGlow with a red color and scaled .5x in the X.

    I was hoping characters A & B could share the same pool. Sure, I can set them each to their own pool and have a pool of green orbs and a pool of red orbs, but this seems incredibly wasteful in terms of memory.

    You're right in that your suggestion seems like the best route. For some reason I was fixated on keeping the functionality in OnEnable to keep the timing very tight, but now that I think about it I'm sure it doesn't really matter. If the particle system starts manually from getting the 'MySpawnController' component from the prefab in the OnSpawn callback and calling some function like 'Play()' on it rather than letting OnEnable on the prefab do the work itself will give me the control I need and shouldn't have any visible difference.

    Thanks again Mel! I really appreciate your insight!
    Will
     
  4. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi Will,

    No worries! :)

    I'm not immediately seeing why setting them up on Awake forces you to put them in different pools but if things are getting too complicated in your setup, the additional pool overhead isn't anything to worry about. PoolKit can readily handle loads of pools and there are some situations where it's actually a good idea to do that!

    Hope this helps! :)

    - Mel
     
  5. saulmcentee

    saulmcentee

    Joined:
    Jul 7, 2015
    Posts:
    15
    Hi, Im using Playmaker and looking at you playmaker examples you are starting with a state called Cache Objects. This state has both Pool Kit Get Pool and Pool kit Get Spawner in it. My question is, when exactly do I need to use the Cache Objects state?
    Can I just use it at the beginning of my game in my Initialise FSM or do I need to use it in every FSM that I am accessing Pool Kit from? Do I even need this? Cant find an explanation for the Cache.

    Thanks for your time.
     
  6. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi @saulmcentee ,

    Apologies for the delay, I'm on British time over here :)

    I just replied to the email you sent via the contact form.

    Hope that helps!

    - Mel
     
  7. OscarLeif

    OscarLeif

    Joined:
    Jul 17, 2016
    Posts:
    68
    Hello I think I could a find a possible bug.

    In the file Pool.cs


    Code (CSharp):
    1. // If the poolItem is setup to reparent objects back to parentRootTransform, do it
    2.                 if( despawnHelperPoolKitInstance.poolItem.keepOrganized &&
    3.                     despawnHelperPoolKitInstance.instanceTransform.parent.GetInstanceID() != _poolItemRootTransformInstanceID
    4.                 ){
    5.                     //despawnHelperPoolKitInstance.instanceTransform.parent = _poolItemRootTransform;
    6.                     despawnHelperPoolKitInstance.instanceTransform.SetParent(_poolItemRootTransform, false);
    7.                 }
    We need to validate if the parent is null.

    Code (CSharp):
    1. if( despawnHelperPoolKitInstance.instanceTransform.parent==null || despawnHelperPoolKitInstance.poolItem.keepOrganized &&
    2.                     despawnHelperPoolKitInstance.instanceTransform.parent.GetInstanceID() != _poolItemRootTransformInstanceID
    3.                 ){
    4.                     //despawnHelperPoolKitInstance.instanceTransform.parent = _poolItemRootTransform;
    5.                     despawnHelperPoolKitInstance.instanceTransform.SetParent(_poolItemRootTransform, false);
    6.                 }
    I never had issues before. Not sure If I make a mistake when Spawn from a Pool that Organize the Gameobjects. I think I change the parent in other script.
     
  8. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi @OscarLeif ,

    Thanks for posting this, and thanks for purchasing PoolKit :)

    However, I don't think I'll roll this into the official release as I believe the issue is you are probably destroying the parent which is supposed to be organising the instances - which you're not supposed to do as you've explicitly told PoolKit to "keep instances organised". The best way to handle this is to either not destroy / modify the parent or you could also try not keeping the instances organised ( which may work if its not a global pool ).

    To clarify, the reason why I'm hesitant to make changes here is adding extra null checks to detect object parents, etc can actually have a very significant impact on performance, especially when dealing with hundreds / thousands of objects. Keeping PoolKit as fast and optimised as possible is one of its design goals so I don't currently think its best to sacrifice performance for an edge case that is essentially messing with the setup. Also, the null check you've implemented in your workaround probably means you will have instances "unorganized", so you've probably broken that setup which is designed to help you preserve instances when changing scenes.

    I hope that makes sense. If you'd like to follow this up, feel free to PM / Email me :)

    Have a great weekend! :)

    Mel
     
    OscarLeif likes this.
  9. OscarLeif

    OscarLeif

    Joined:
    Jul 17, 2016
    Posts:
    68
    I follow the recommendation I rollback the changes.
    Issue: one of my scripts change the parent to null after spawn the Game object.

    Thanks for the explanation. Oscar Leif.
     
    melgeorgiou likes this.
  10. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi @OscarLeif ,

    Ah, that could explain it :)

    No problem, have a great week! :)

    - Mel
     
  11. OscarLeif

    OscarLeif

    Joined:
    Jul 17, 2016
    Posts:
    68
    Hello I'm curious if we can expand a Pool is possible to shrink the pool ?
    How we can do this ?
     
    Last edited: May 7, 2023
  12. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi @OscarLeif ,

    Generally, PoolKit doesn't do this because it's almost always an unnecessary memory allocation ( which causes stuttering - one of the main problems PoolKit is designed to solve ).

    The way it is usually handled ( at least with local pools ) is that at the start of every scene, the pool automatically returns to its default size. That way the extra slots are cleaned up at a time that doesn't cause issues with core gameplay.

    Hope that helps! :)

    - Mel
     
  13. caseyfarina

    caseyfarina

    Joined:
    Dec 22, 2016
    Posts:
    8
    Hi Mel,
    After looking at the documentation, I'm not quite sure what the fastest method is for returning all of the active prefabs in a pool. This would be for taking a more data oriented design approach to iterating over the active pool prefabs. You seem to warn against using the "getActiveInstances" in the documentation.
    Thanks!
     
  14. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    762
    Hi @caseyfarina,

    That API method basically returns a copy of the internal PoolKit arrays. its memory intensive and will create garbage, etc. It is designed for situations where you need to run a one-off operation on all the current instances but not really something you'd want to run every frame.

    I'm not exactly sure why you'd want or need this as PoolKit handles this for you internally anyway. One thing you could do is create a local list of instances every time you spawn them from script, etc. Perhaps if you want to describe what you are trying to achieve via PM/email I can offer some suggestions for tomorrow. Please include your invoice ID! :)

    Hope that helps!

    - Mel
     
    Last edited: Sep 27, 2023