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 Search for specific entity(s) based on component values

Discussion in 'Entity Component System' started by JamesWjRose, May 12, 2023.

  1. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663
    Hello gang,

    I am lost, there seems to not be a way to search for specific entities. There is the Entity Query where I can create a list of entities that contain a specific component, but not by the values of said component. Am I wrong?

    I started with databases back in the early 90s, so the ability to search for specific items is so common. It exists for databases, arrays, etc. So what dumbass thing am *I* missing?

    Thanks
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Nothing really built in. You need to implement it yourself. Why? Well because most of the time you should immediately process each result you discover to save memory bandwidth. However, if the number of unique values is small, you might benefit from shared components.
     
    JamesWjRose likes this.
  3. Occuros

    Occuros

    Joined:
    Sep 4, 2018
    Posts:
    284
    In General System I would agree, but for the editor it would be really useful if we could not just query for entities with specific components, but actual specified values.

    It would make the inspection of entities a lot more pleasant/faster.
     
    JamesWjRose likes this.
  4. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    You can write quick job, which looks up through queries entities with specific value.
     
  5. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663
    You can post an example, all I can find is jobs that return components by type, not by content/value.
     
  6. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663
    Thanks, at least I am not insane for not finding an answer that does not exist.
     
  7. Razmot

    Razmot

    Joined:
    Apr 27, 2013
    Posts:
    345
    Make yourself a DebugTools.cs MonoBehaviour with this kind of methods :

    Code (CSharp):
    1. [ContextMenu("AnalyzeMeshStats")]
    2.         public void AnalyzeMeshStats()
    3.         {
    4.             var em = World.DefaultGameObjectInjectionWorld.EntityManager;
    5.             var entities = em.GetAllEntities(Allocator.Temp);
    6.  
    7.             int minV = int.MaxValue;
    8.             int maxV = 0;
    9.  
    10.             bool badOnlyBorders = true;
    11.             bool goodOnlyBorders = true;
    12.  
    13.             foreach (var e in entities)
    14.             {
    15.                 if (em.HasComponent<MeshStats>(e))
    16.                 {
    17.                     var stats = em.GetComponentData<MeshStats>(e);
    18.                     minV = min(minV, stats.VertCount);
    19.                     maxV = max(maxV, stats.VertCount);
    20.  
    21.                     var voxStats = em.GetComponentData<VoxStats>(e);
    22.  
    23.                     if (em.HasComponent<BadVertices>(e))
    24.                     {
    25.                         badOnlyBorders &= stats.OnlyCrossingBorder;
    26.                     }
    27.                     else
    28.                     {
    29.                         goodOnlyBorders &= stats.OnlyCrossingBorder;
    30.                     }
    31.                 }
    32.             }
    33.  
    34.             Debug.Log($"min vert count{minV} , max {maxV}");
    35.             Debug.Log($"BadMeshes onlyBorders {badOnlyBorders}");
    36.             Debug.Log($"Good Meshes onlyBorders {goodOnlyBorders}");
    37.  
    38.         }
    It's extremely fast to scan 10,000ds of entities, it's all small stuff in memory !
     
  8. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663
    Thanks, but that's not what I am asking for (but REALLY, thank you!) All I need is the ability to filter:

    ie:
    Code (CSharp):
    1. var entities = em.GetAllEntities(Allocator.Temp);
    2.  
    3. var subset = entities.Where(x => x.value = 2)
    I just cannot believe that ANYONE would build a data store and not have a search function. BLOWS MY MIND!

    Anyway, have a wonderful weekend
     
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    There's not a good construct for this in the C# language that is also Burst-friendly. And as I said earlier, you typically wouldn't want to do things this way if you care about performance.
     
  10. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    As mentioned, searching across all entities for specific value in components is inefficient and slow.
    If you want it as an editor tooling - you can build it.

    If you want it for game logic - forget it.
    Define a query you need to search on, and then do a manual search inside specified component.
    Code (CSharp):
    1. Entities.ForEach((T someComponent) => {
    2.     if (someComponent.Value == X)
    3.          // Result found
    4. }).Schedule();
    Its basically the same thing you're asking for.

    Alternatively, think of a different solution based on your specific problem.
    It depends on what you're trying to solve.
    From my experience, there's literally 0 times such search would been required.

    There are multiple approaches how to outline specific entity in context of game logic.
    You can do any of the following:
    - Enable / Disable it (to use enable mask / filter on the query);
    - Put a flag inside the component, process all component of <T> with that flag;
    - Set a tag to the entity via ECB;
    (identical to enabling / disabling but without thread write, and slower due to structural changes);
    - Add specific entity to the native container to process it later;
    - Read / Write separate entity directly via lookups;
    - Use shared component as a key; (Though its not worth doing in most of the cases)

    ECS is not exactly database.
    While having similar concepts (due to being DOD in nature) its not 1 to 1 representation.
    Its built to process large quantities of data in batches at blazing fast speeds over defined and O(1) queries.

    At the same time, you'd want to place constraints in the database query to find specifically what you want anyway, so...
     
    Last edited: May 13, 2023
  11. SillyConesValley

    SillyConesValley

    Joined:
    May 13, 2023
    Posts:
    6
    It would be good to have an example use case of this, that we can reason about. Maybe the best solution might not even have to involve "querying by value", but would take a different approach that flips the problem on its head
     
    Last edited: May 13, 2023
  12. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663
    Ok gang,

    I am going to try this again in hopes that someone has an answer:

    I have roads, connections (aka Intersections) and autos. The roads have Vector3 points for each lane. Each auto is assigned a Road and therefore it's points. When an auto reaches the end of a road, the Connection/Intersection has data that shows me which roads are connected to it. I need to gather these other roads, choose one of them at random and then have the Auto move along that new road's points.

    There is a set of entities for Roads and another set of entities for Connections. In my old, pre 1.0 ECS I had two entity queries that gave me all Roads and Connections (one NativeArray for Roads, one for Connections)

    When the Auto reached the end of the Road I would look through the Connections to find a match to the identity at the end of the Road (asset: Easy Roads 3D, good asset, AWESOME support!) When I get that Connection I can then find it's "exit" point Road Identities, those that met that criteria are then put into a list that I can randomly choose from.

    I "need" this functionality because there should be random movement of the autos. I'd hate to assign an Auto's path at startup and then just have it do that "loop"

    If anyone has a better idea on how to model this, I am absolutely open to ideas.
     
  13. SillyConesValley

    SillyConesValley

    Joined:
    May 13, 2023
    Posts:
    6
    I assume this will need further discussion or explanations of details that I've missed, but let's start here:
    1. CarSystem makes car entities progress along their current Road.
    2. CarSystem also handles detecting when a car has reached the end of a road
      1. When that happens, it gets that road's Intersection entity (presumably stored in a component on the road entity)
      2. On the Intersection entity, it gets a DynamicBuffer of connected Roads entities
      3. It looks through those connected roads and chooses a random valid match. This is where the car goes next
     
    JamesWjRose likes this.
  14. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663
    Sounds like you have the idea. Here is my old code: https://github.com/Blissgig/Easy-Road-3D-ECS-Traffic
     
  15. SillyConesValley

    SillyConesValley

    Joined:
    May 13, 2023
    Posts:
    6
    In that case I don't think you need any querying by value to solve this (aside from a general car query to update car entities)

    - Cars store the entity for their current road in the Car component
    - Roads store the entity(ies) of their connections in a DynamicBuffer
    - Connections store the entities of connected roads in a DynamicBuffer

    So when a car reaches the end of a road, it can go find a random next connected road through these stored entities. Car gets road entity, then finds intersections of that road, then finds other roads. All this just by looking up components or buffers on entities starting from the Car entity
     
    JamesWjRose likes this.
  16. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663

    huh.... interesting. I will absolutely digest this. Thank you very much.
     
  17. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    So basically you want to find closest conector(s) to the next road at the end of the path?

    You could just store / insert connection points (start / end) in some kind of auxilary structure, like an octree / quadtree.
    E.g. https://github.com/marijnz/NativeOctree
    (actual tree works fine in recent versions with some changes to the test API)

    Then query by car position + radius / distance will return all result connectors.

    That will immensely reduce random access.
    If your map doesn't change often, it will be also faster, since no tree rebuilding would be required.

    Plus it will reduce headache from keeping all the references and iterating through them.
    +
    Edit:
    Manually connecting roads will also work (by storing refs in DynamicBuffer of the road).
    But having to manually change connections [instead of just adjusting road points] is probably be more taxing on the game design side.
     
    Last edited: May 13, 2023
  18. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    663

    I will look into this. Thank you very much
     
    xVergilx likes this.