Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    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:
    768
    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:
    768
    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:
    768
    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:
    69
    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:
    768
    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:
    69
    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:
    768
    Hi @OscarLeif ,

    Ah, that could explain it :)

    No problem, have a great week! :)

    - Mel
     
  11. OscarLeif

    OscarLeif

    Joined:
    Jul 17, 2016
    Posts:
    69
    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:
    768
    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:
    768
    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
  15. Njordy

    Njordy

    Joined:
    Mar 31, 2019
    Posts:
    24
    Hi. Actually just wanted to check if the Kits were participating in Cyber Week and I missed it like an i**t, or not?
    And have you thought about maybe making a bundle out of all 4 kits (like Amplify did for example)? :)
     
  16. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    768
    Hi @Njordy ,

    Thanks for your interest! :)

    The good news is you didn't miss it, haha. Unity's Asset Store team are in charge of selecting assets for sale so unfortunately that is out of my control.

    However, creating a discounted all-in-one pack is a great idea! :)

    I'll try and set it up tomorrow. I'll throw Mesh Kit, Interaction Kit, Pool Kit and Data Kit in the pack at ~25% off.

    Thanks for the idea and hope that helps! :)

    - Mel
     
    Last edited: Dec 2, 2023
    Njordy likes this.
  17. TimothyHao

    TimothyHao

    Joined:
    Nov 18, 2019
    Posts:
    1
    Hello,

    I'm considering implementing PoolKit in my project due to a large number of dynamically generated effects, which vary greatly in type and quantity. 1.My approach to using PoolKit involves dynamically adding the necessary prefabs into the pool, which will result in a significant number of prefabs within PoolKit. 2.Additionally, with each scene transition, I plan to unload many resources from the pool to prevent resource overload. However, not all resources can be unloaded since some will continue to be used after the scene change.

    Do you foresee any issues with this approach?

    Furthermore, I'm contemplating categorizing pools by type, such as sound, effects (like attack, hit, etc.), enemies, and bullets, with one pool for each category. Does this seem like a viable strategy?

    I appreciate your insights on this matter. Thank you very much.
     
  18. melgeorgiou

    melgeorgiou

    Joined:
    Nov 3, 2012
    Posts:
    768
    Hi @TimothyHao,

    Firstly, thanks for your interest in PoolKit! :)

    If you're building your pools via the API, in general you should be ok with this approach. but for optimal performance I'd recommend considering the following:

    - If you have a very huge number of prefabs / instances, splitting up your Pools by category will probably be beneficial. After the first frame (where PoolKit sets everything up), there is virtually no overhead in having multiple pools, but there is a slight benefit in optimising instance selection if you break up the pools into a more manageable size. The performance difference in either approach should be very minor, though, so feel free to do what makes the most sense for your project.

    - Breaking up your pools into well thought out categories also naturally helps you get rid of the pools you don't want, and seamlessly helps you setup each scene with the prefabs it needs.

    - If there are prefabs you want across the whole game, you can also setup a global pool for those so you won't need to unload them or even need to manage it. But if everything you're building is dynamically created, then you'd probably need to stick with the API approach.

    Hope this helps! :)

    - Mel