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

Question Implementing object pooling together with random bag

Discussion in 'Scripting' started by GamesbyJP, May 16, 2023.

  1. GamesbyJP

    GamesbyJP

    Joined:
    May 20, 2018
    Posts:
    62
    I am trying to implement object pooling with a random bag as an exercise, let's say for a recreation of Tetris for this example.

    What I am trying to accomplish is to not create or destroy a new Tetris block every time I need one, so for this I want to use the UnityEngine.ObjectPool API.
    I also want to make sure that the Tetris blocks are truly random and eliminate the chance of having bad luck and receiving that L block you desparately need after only a hundred new blocks.
    For this, I plan to use a random bag implementation from this github repository:

    As of now I do the following:

    1) Create a randomBag and fill it until it contains each Tetris piece x times.

    2) Fill the ObjectPool with each item of this randomBag.

    This is where I am stuck, as calling ObjectPool.Get() returns the last item of the internal ObjectPool collection and calling ObjectPool.Release(item) adds it to the end of the collection, which means that I would only truly get a random item if I exhaust all items from the ObjectPool, and I won't.
    Calling RandomBag.PopRandomItem() would return a truly random item, but I would need to call ObjectPool.Get(item) (which would look like ObjectPool.Get(RandomBag.PopRandomItem()) in code) to receive that specific item from the object pool.
    There are a few problems with that:

    1) ObjectPool.Get() doesn't take a specific item as a parameter

    2) Implementing an ObjectPool.Get(item) method is possible in theory, but that would mean that I have to jump through all sorts of hoops to get access to the internal collection of the ObjectPool class, or that I would have to write my own implementation from scratch.

    3) The implementation of an ObjectPool.Get(item) would require iterating over the entire ObjectPool collection (in a worst case scenario), which would not be great for performance if the collection is large.

    4) I would also need a way to shuffle the internal collection of the ObjectPool, which comes with the same problems as 2).

    Any help or insight on how to solve this problem would be very helpful!
     
  2. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    Hello,

    Why don't you create your own object pool with a simple List<TetrisPiece> ? That would allow you to do all your custom logic.
     
    Kurt-Dekker likes this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Object pooling is trivial. It's just a list of available, a list of used, and an API to return / retrieve it.

    Any more doesn't belong in a pool. A system using a pool should work even if you disable the pool (eg, set its size to zero).

    Otherwise, it's not a pool, it's a critical part of your business logic.

    Same applies to a cache.

    I appreciate the desire to work through typical CIS-100 programming challenges like this but just know that using them in actual production practices is EXTREMELY destructive to the average project, making everything three or four times as complicated and introducing MASSIVE surface for difficult-to-diagnose bugs.

    As of yesterday I spent the entire morning tracking down what was an object pooling bug in our scrolling front menu... this was an attempt to pool ten (10) little buttons to recycle them and the buttons had grown into a nightmare of saved state that simply was not being restored. Stay away from pooling. It's AWFUL.

    The costs and issues associated with object pooling / pools:

    https://forum.unity.com/threads/object-pooling.1329729/#post-8405055

    https://forum.unity.com/threads/object-pooling-in-a-tower-defense-game.1076897/#post-6945089
     
    GamesbyJP likes this.
  4. GamesbyJP

    GamesbyJP

    Joined:
    May 20, 2018
    Posts:
    62
    Because I haven't figured out what the best approach to solving this problem is as of now.
    As Kurt mentioned, I don't want the pool to turn into a critical part of the system but I haven't found an elegant solution so far.
     
  5. GamesbyJP

    GamesbyJP

    Joined:
    May 20, 2018
    Posts:
    62
    This is not some CIS-100 challenge but a problem I'm attempting to solve as elegantly as possible for myself.
    If I end up with the same conclusion as you that's fine, but I'd like to try and see for myself rather than not try and make assumptions from something I ended up not doing because I read somewhere it was too complicated and not worth it.

    I appreciate the response and I am taking note of your warning though, don't get me wrong.
    For now I am trying to implement this random bag with object pooling in a small game I made and released already, so I know the code inside and out. This problem has been bugging me lately since I can't seem to come up with a solution though, hence why I came here.
     
    MaskedMouse likes this.
  6. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    I dont really see your point, as you do actually use UnityEngine.ObjectPool API, making your custom pooling won't make it more critical than it is now :p
     
  7. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,105
    You could have the RandomBag just contain the types of the different blocks, and then use that to map to an object pool that is unique for that block type.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine.Pool;
    4. using Object = UnityEngine.Object;
    5.  
    6. public sealed class RandomPool<TObject> where TObject : Object
    7. {
    8.     private readonly Dictionary<Type, ObjectPool<TObject>> pools;
    9.     private readonly RandomBag<Type> nextTypes;
    10.  
    11.     public RandomPool(params TObject[] prefabs)
    12.     {
    13.         int count = prefabs.Length;
    14.         pools = new(count);
    15.         var types = new Type[count];
    16.  
    17.         for(int i = 0; i < count; i++)
    18.         {
    19.             TObject prefab = prefabs[i];
    20.             Type type = prefab.GetType();
    21.             pools.Add(type, CreatePool(prefab));
    22.             types[i] = type;
    23.         }
    24.  
    25.         nextTypes = new(types);
    26.     }
    27.  
    28.     public void Get() => pools[nextTypes.PopRandomItem()].Get();
    29.     public void Release(TObject instance) => pools[instance.GetType()].Release(instance);
    30.  
    31.     public ObjectPool<TObject> CreatePool(TObject prefab)
    32.     {
    33.         ...
     
    Last edited: May 21, 2023
    GamesbyJP likes this.
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I just want to clarify this: this is not true randomness, this is tampered randomness, or distributed randomness. Yes the statistics won't change in a sufficiently large sample, but it will drastically change locally (as you yourself stated), as is the case with any kind of token pooling. True randomness is getting 1000 of the same item over and over, however improbable that might seem. The same outcome can theoretically repeat infinitely many times, and who knows, maybe we live in a Universe where this has already happened, and we call this phenomenon "the laws of physics".

    Anyway we humans dislike this property a lot, because we wrongly assume true randomness to be nearly homogeneous. Nothing wrong with the approach, it's just the nomenclature.
     
    GamesbyJP and SisusCo like this.
  9. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    You may find some interest in that module I created Fair Random
     
    orionsyndrome likes this.
  10. GamesbyJP

    GamesbyJP

    Joined:
    May 20, 2018
    Posts:
    62
    Having a pool for each block type looks like a good solution at first glance..
    I'll give it a try as soon as I have the time, thanks!
     
    SisusCo likes this.
  11. GamesbyJP

    GamesbyJP

    Joined:
    May 20, 2018
    Posts:
    62
    To conclude this thread, I implemented the solution @SisusCo proposed and garbage allocation has been reduced while I make good use of the random bag. Pretty straightforward solution, thank you.
    @Kurt-Dekker you were right for warning me regarding object pooling. Took me a few hours of troubleshooting before I fixed all sorts of weird issues caused by having to reset the state of the object when returning it to the pool. I guess the takeaway is that object pooling is just not worth implementing unless performance is noticeably impacted by garbage collection, just takes too much managing and therefore time overall.