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

Search throw many components with good performance

Discussion in 'Scripting' started by Elivard, May 27, 2022.

  1. Elivard

    Elivard

    Joined:
    Apr 1, 2020
    Posts:
    10
    Hi!

    I was wondering what is the most efficient way to search for a list or a single component throw my game.

    As an example I have lots of units and I would like to search for all units that have a giving item in their inventory.

    For the moment I have a component property that retain all the unit GameObject.
    Then I can make a search that look like

    Code (CSharp):
    1.     public IEnumerable<T> GetUnitWithComponent<T>() where T : MonoBehaviour
    2.     {
    3.         return units.Where(u => u.GetComponent<T>() != null).Select(x => x.GetComponent<T>());
    4.     }
    After that I do another Linq query to match what I want.

    Would it be a good idea to not only retains all unit GameObject but list of components?
    Here is an example with inventories but I would do it with other components.

    Code (CSharp):
    1.     public IEnumerable<T> GetUnitInventories<T>(Func<Inventory, bool> query) where T : MonoBehaviour
    2.     {
    3.         return inventories.Where(x => query(x));
    4.     }
    Is it a good idea?
    What would be the best way to do it performance wise?
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Well, at the risk of sounding like some stack overflow snob: it depends. You're not going to notice it on the profiler if you're searching a hundred units, once or twice a minute, and iterating over the raw game object fetching random components to query their states. But, if you're executing these queries every second on a collection of 5000 units, then my advice would be different.

    I would keep going with the first method if your collection is small and infrequently queried, as it avoids all the overhead and boilerplate of tracking a bunch of individual components you rarely need to sift through.
     
    Elivard likes this.
  3. Elivard

    Elivard

    Joined:
    Apr 1, 2020
    Posts:
    10
    Thank you for the answer.

    To add a bit of details I'm working on a city builder. Sometimes (not every frame) an unit will try to find a new job like working on a new building or delivering resources to it. To do it the unit will search throw all buildings to find if some have a construction parameter or an inventory that need resources.

    So I'm afraid that looping throw all the buildings and testing if they have x or y component will be super heavy. I can't find a way to make this system clean and performance wise.

    I'm also thinking about GameObject component that add themself to a list to say "I need builder" or "I need resources" so the unit doesn't event have to search because list is ready.
    But this way I will have a lot of different list. One for each "need".
     
  4. Karraticus

    Karraticus

    Joined:
    May 24, 2022
    Posts:
    3

    I'd suggest going with a middleman script which receives two sets of inputs and creates two lists; buildings with open requests and available workers. Have the script only search this list and pair requests to workers based on whatever parameters you want. You can even have it do a final check/filter where it considers workers closest to the job sites as priority candidates.

    This way, the worker and building only need to submit their requests once and the script will handle pairing from the available options within its own truncated list.
     
    Elivard likes this.
  5. Elivard

    Elivard

    Joined:
    Apr 1, 2020
    Posts:
    10
    OK, thank you for this advise!
     
  6. Peeling

    Peeling

    Joined:
    Nov 10, 2013
    Posts:
    404
    Your instincts are good here. The best way to search for something is not to have to.

    Consider using generics, if you have a lot of lists to manage - something along these lines perhaps:

    Code (CSharp):
    1.  
    2. public class Needs<T>
    3. {
    4.     static List<ClientClass> clients = new List<ClientClass>();
    5.  
    6.     public static void Add(ClientClass c)
    7.     {
    8.         clients.Add(c);
    9.     }
    10.  
    11.     public static void Remove(ClientClass c)
    12.     {
    13.         clients.Remove(c);
    14.     }
    15.  
    16.     public static ClientClass Request()
    17.     {
    18.         if (clients.Count > 0) return clients[0]; // up to the client to unregister
    19.         return null;
    20.     }
    21. }
    22.  
    Now you can just say:

    Code (CSharp):
    1. Needs<Wood>.Add(this);
    If you have a lot of clients, you might want to explore other, more efficient options for the underlying data structure.

    Later on, you can update that class to support finding nearest, add bucketing to make finding nearest more efficient - and it will just work with every 'need' you've identified.

    You could also implement a similar Has<T> class for providers, and even have them liaise with each other so that providers and clients are immediately offered a match as they register, if one is available.
     
    Last edited: May 28, 2022