Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Question about EntityArchetypeQuery

Discussion in 'Entity Component System and C# Job system' started by SubPixelPerfect, Nov 22, 2018.

  1. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
    I have entities of 3 archetypes (A) (B) & (AB)
    and I wonder how to build a query that will return only (A) and (B) without (AB) ?

    Code (CSharp):
    1. |   To Query             |    Use                |
    2. |------------------------|-----------------------|      
    3. |   only (A):            |    all(A) none(B)     |      
    4. |   only (B):            |    all(B) none(A)     |      
    5. |   only (AB):           |    all(A,B)           |
    6. |   only (A)&(B):        |    ????????????       |    
    7. |   only (A)&(AB):       |    all(A)             |
    8. |   only (B)&(AB):       |    all(B)             |
    9. |   all  (A)&(B)&(AB):   |    any(A,B)           |  
    the only way I found is to make 2 queries and combine their results, is it the only way?
     
    Last edited: Nov 22, 2018
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,153
    I'd probably do Any and filter them out

    Code (CSharp):
    1.         public struct A : IComponentData { }
    2.         public struct B : IComponentData { }
    3.  
    4.         private ComponentGroup abQuery;
    5.  
    6.         protected override void OnCreateManager()
    7.         {
    8.             this.abQuery = this.GetComponentGroup(new EntityArchetypeQuery
    9.             {
    10.                 Any = new[] { ComponentType.Create<A>(), ComponentType.Create<B>(), },
    11.                 None = Array.Empty<ComponentType>(),
    12.                 All = Array.Empty<ComponentType>(),
    13.             });
    14.         }
    15.  
    16.         protected override JobHandle OnUpdate(JobHandle handle)
    17.         {
    18.             return new AorBJob()
    19.             {
    20.                 AType = this.GetArchetypeChunkComponentType<A>(),
    21.                 BType = this.GetArchetypeChunkComponentType<B>(),
    22.             }.Schedule(this.abQuery, handle);
    23.         }
    24.  
    25.         [BurstCompile]
    26.         private struct AorBJob : IJobChunk
    27.         {
    28.             public ArchetypeChunkComponentType<A> AType;
    29.             public ArchetypeChunkComponentType<B> BType;
    30.  
    31.             /// <inheritdoc />
    32.             public void Execute(ArchetypeChunk chunk, int chunkIndex)
    33.             {
    34.                 var aArray = chunk.GetNativeArray(this.AType);
    35.                 var bArray = chunk.GetNativeArray(this.AType);
    36.  
    37.                 var hasA = aArray.Length > 0;
    38.                 var hasB = bArray.Length > 0;
    39.  
    40.                 if (hasA && hasB)
    41.                 {
    42.                     return;
    43.                 }
    44.  
    45.                 for (var i = 0; i < chunk.Count; i++)
    46.                 {
    47.                     // only A || B not A & B
    48.  
    49.                 }
    50.             }
    51.         }
    Shouldn't cause too many performance issues.
     
  3. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
    until you have 500000 (AB) entities to skip :)
     
  4. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    3,036
    AFAIK either that or duplicate your system and filter for A && !B and B && !A cases.

    The filtering is very basic as of now, Joachim (somewhere deep in the forums) told that they will work on it after the base API will somewhat stable (to simplify corner cases like this), so it may become possible without either duplicate or skip entities in the future.
     
  5. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
    By the way, I made a simple helper class to build EntityArchetypeQuery

    with it, you can do it this way
    Code (CSharp):
    1. _query1 = new QueryBuilder().Any<A,B,C>().Build;
    2. _query2 = new QueryBuilder().All<A,B>().None<C>().Build;
    or, after extending ComponentSystem class even nicer
    Code (CSharp):
    1. _query1 = Query.Any<A,B,C>().Build;
    2. _query2 = Query.All<A,B>().None<C>().Build;

    what do you think?


    here is the source
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Unity.Entities;
    3.  
    4. public abstract class ECSystem : ComponentSystem{
    5.   protected abstract override void OnUpdate();
    6.   public QueryBuilder Query{
    7.     get{return new QueryBuilder();}
    8.   }
    9. }
    10.  
    11. public class QueryBuilder{
    12.  
    13.   private List<ComponentType> _all = new List<ComponentType>();
    14.   private List<ComponentType> _any = new List<ComponentType>();
    15.   private List<ComponentType> _none = new List<ComponentType>();
    16.  
    17.   public QueryBuilder(){}
    18.  
    19.   public static QueryBuilder New => new QueryBuilder();
    20.  
    21.   public EntityArchetypeQuery Build => new EntityArchetypeQuery(){
    22.     All = _all.ToArray(),
    23.     None = _none.ToArray(),
    24.     Any = _any.ToArray()
    25.   };
    26.  
    27.   public QueryBuilder All<T>()
    28.     where T:struct, IComponentData
    29.   {
    30.     _all.Add(ComponentType.Create<T>());
    31.     return this;
    32.   }
    33.   public QueryBuilder All<T,T2>()
    34.     where T:struct, IComponentData
    35.     where T2:struct, IComponentData
    36.   {
    37.     _all.Add(ComponentType.Create<T>());
    38.     _all.Add(ComponentType.Create<T2>());
    39.     return this;
    40.   }
    41.   public QueryBuilder All<T,T2,T3>()
    42.     where T:struct, IComponentData
    43.     where T2:struct, IComponentData
    44.     where T3:struct, IComponentData
    45.   {
    46.     _all.Add(ComponentType.Create<T>());
    47.     _all.Add(ComponentType.Create<T2>());
    48.     _all.Add(ComponentType.Create<T3>());
    49.     return this;
    50.   }
    51.   public QueryBuilder All<T,T2,T3,T4>()
    52.     where T:struct, IComponentData
    53.     where T2:struct, IComponentData
    54.     where T3:struct, IComponentData
    55.     where T4:struct, IComponentData
    56.   {
    57.     _all.Add(ComponentType.Create<T>());
    58.     _all.Add(ComponentType.Create<T2>());
    59.     _all.Add(ComponentType.Create<T3>());
    60.     _all.Add(ComponentType.Create<T4>());
    61.     return this;
    62.   }
    63.   public QueryBuilder All<T,T2,T3,T4,T5>()
    64.     where T:struct, IComponentData
    65.     where T2:struct, IComponentData
    66.     where T3:struct, IComponentData
    67.     where T4:struct, IComponentData
    68.     where T5:struct, IComponentData
    69.   {
    70.     _all.Add(ComponentType.Create<T>());
    71.     _all.Add(ComponentType.Create<T2>());
    72.     _all.Add(ComponentType.Create<T3>());
    73.     _all.Add(ComponentType.Create<T4>());
    74.     _all.Add(ComponentType.Create<T5>());
    75.     return this;
    76.   }
    77.  
    78.  
    79.   public QueryBuilder None<T>() where T:struct, IComponentData{
    80.     _none.Add(ComponentType.Create<T>());
    81.     return this;
    82.   }
    83.   public QueryBuilder None<T,T2>()
    84.     where T:struct, IComponentData
    85.     where T2:struct, IComponentData{
    86.     _none.Add(ComponentType.Create<T>());
    87.     _none.Add(ComponentType.Create<T2>());
    88.     return this;
    89.   }
    90.   public QueryBuilder None<T,T2,T3>()
    91.     where T:struct, IComponentData
    92.     where T2:struct, IComponentData
    93.     where T3:struct, IComponentData{
    94.     _none.Add(ComponentType.Create<T>());
    95.     _none.Add(ComponentType.Create<T2>());
    96.     _none.Add(ComponentType.Create<T3>());
    97.     return this;
    98.   }
    99.   public QueryBuilder None<T,T2,T3,T4>()
    100.     where T:struct, IComponentData
    101.     where T2:struct, IComponentData
    102.     where T3:struct, IComponentData
    103.     where T4:struct, IComponentData
    104.   {
    105.     _none.Add(ComponentType.Create<T>());
    106.     _none.Add(ComponentType.Create<T2>());
    107.     _none.Add(ComponentType.Create<T3>());
    108.     _none.Add(ComponentType.Create<T4>());
    109.     return this;
    110.   }
    111.   public QueryBuilder None<T,T2,T3,T4,T5>()
    112.     where T:struct, IComponentData
    113.     where T2:struct, IComponentData
    114.     where T3:struct, IComponentData
    115.     where T4:struct, IComponentData
    116.     where T5:struct, IComponentData
    117.   {
    118.     _none.Add(ComponentType.Create<T>());
    119.     _none.Add(ComponentType.Create<T2>());
    120.     _none.Add(ComponentType.Create<T3>());
    121.     _none.Add(ComponentType.Create<T4>());
    122.     _none.Add(ComponentType.Create<T5>());
    123.     return this;
    124.   }
    125.  
    126.  
    127.  
    128.   public QueryBuilder Any<T>() where T:struct, IComponentData{
    129.     _any.Add(ComponentType.Create<T>());
    130.     return this;
    131.   }
    132.   public QueryBuilder Any<T,T2>()
    133.     where T:struct, IComponentData
    134.     where T2:struct, IComponentData{
    135.     _any.Add(ComponentType.Create<T>());
    136.     _any.Add(ComponentType.Create<T2>());
    137.     return this;
    138.   }
    139.   public QueryBuilder Any<T,T2,T3>()
    140.     where T:struct, IComponentData
    141.     where T2:struct, IComponentData
    142.     where T3:struct, IComponentData{
    143.     _any.Add(ComponentType.Create<T>());
    144.     _any.Add(ComponentType.Create<T2>());
    145.     _any.Add(ComponentType.Create<T3>());
    146.     return this;
    147.   }
    148.   public QueryBuilder Any<T,T2,T3,T4>()
    149.     where T:struct, IComponentData
    150.     where T2:struct, IComponentData
    151.     where T3:struct, IComponentData
    152.     where T4:struct, IComponentData
    153.   {
    154.     _any.Add(ComponentType.Create<T>());
    155.     _any.Add(ComponentType.Create<T2>());
    156.     _any.Add(ComponentType.Create<T3>());
    157.     _any.Add(ComponentType.Create<T4>());
    158.     return this;
    159.   }
    160.   public QueryBuilder Any<T,T2,T3,T4,T5>()
    161.     where T:struct, IComponentData
    162.     where T2:struct, IComponentData
    163.     where T3:struct, IComponentData
    164.     where T4:struct, IComponentData
    165.     where T5:struct, IComponentData
    166.   {
    167.     _any.Add(ComponentType.Create<T>());
    168.     _any.Add(ComponentType.Create<T2>());
    169.     _any.Add(ComponentType.Create<T3>());
    170.     _any.Add(ComponentType.Create<T4>());
    171.     _any.Add(ComponentType.Create<T5>());
    172.     return this;
    173.   }
    174. }
     
    Last edited: Feb 7, 2019
    e199 likes this.
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,153
    You're skipping chunks not individual entities so it's not as bad as it sounds.

    I decided to check for myself so I wrote a quick benchmark

    Benched at in a standalone build, though due to lack of safety checks in the bench the performance was very similar in editor - actually strangely it was often better in editor. Probably the attached profiler.

    500,001 entities (166667 A, B and AB)

    Single Query to filter out combos (similar to above code) vs Two queries / two jobs.

    I realized after writing this up I badly named my jobs.
    DoubleQueryTest is a single query of AB and filtering out AB
    SingleQueryTest is two single queries and separate jobs to handle them.

    upload_2018-11-23_13-29-52.png

    upload_2018-11-23_13-30-15.png

    upload_2018-11-23_13-30-29.png

    Pretty consistant, overall if you factor in the cost of the job slicing they're pretty much the same.

    But clearly 500k entities is not enough to really stress it.
    Let's try 5 million (and 1) entities

    upload_2018-11-23_13-36-49.png

    upload_2018-11-23_13-37-3.png

    Again, nearly identical performance. The check causes no detectable degradation.

    Just for giggles, tried 50 million (and 1) entities
    but the unity build crashed, would not create 50mill entities =(

    TLDR: i very much doubt that very simple check is ever going to cause you roadblock unless you have your data in way too many separate chunks.
     
  7. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,153
    Actually I just thought of another alternative if you really don't want to do the check.

    Have a AB component that just acts as subtraction on your query

    Code (CSharp):
    1.             this.abQuery = this.GetComponentGroup(new EntityArchetypeQuery
    2.             {
    3.                 Any = new[] { ComponentType.Create<A>(), ComponentType.Create<B>(), },
    4.                 None = new[] { ComponentType.Create<AB>(),
    5.                 All = Array.Empty<ComponentType>(),
    6.             });
    Which you can add with this query

    Code (CSharp):
    1.             this.abQuery = this.GetComponentGroup(new EntityArchetypeQuery
    2.             {
    3.                 Any = Array.Empty<ComponentType>(),
    4.                 None = new[] { ComponentType.Create<AB>(),
    5.                 All = new[] { ComponentType.Create<A>(), ComponentType.Create<B>(), },
    6.             });
    You'd need 2 queries to remove it though.
    As long as you're not constantly adding/removing A/B should work pretty well.

    But yeah, stand by my filter check above~
    Much cleaner.
     
  8. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
    Here is how I did it with AddMatchingArchetypes
    Code (CSharp):
    1.  
    2. public class CompoundQueryJobChunkIterationSys : JobComponentSystem {
    3.   private EntityArchetypeQuery _q1;
    4.   private EntityArchetypeQuery _q2;
    5.  
    6.   protected override void OnCreateManager() {
    7.     _q1 = new QueryBuilder().Any<A>().None<B>().Build;
    8.     _q2 = new QueryBuilder().Any<B>().None<A>().Build;
    9.   }
    10.  
    11.   protected override JobHandle OnUpdate(JobHandle inputDeps) {
    12.    
    13.     var foundArchetypes = new NativeList<EntityArchetype>(Allocator.TempJob);
    14.     EntityManager.AddMatchingArchetypes(_q1, foundArchetypes);
    15.     EntityManager.AddMatchingArchetypes(_q2, foundArchetypes);
    16.     var chunks = EntityManager.CreateArchetypeChunkArray(foundArchetypes, Allocator.TempJob);
    17.     foundArchetypes.Dispose();
    18.    
    19.     Job job = new Job() {
    20.       aChunkType = GetArchetypeChunkComponentType<A>(),
    21.       bChunkType = GetArchetypeChunkComponentType<B>(),
    22.       chunks = chunks
    23.     };
    24.  
    25.     return job.Schedule(chunks.Length, 64, inputDeps);
    26.   }
    27.  
    28.   private struct Job : IJobParallelFor {
    29.    
    30.     public ArchetypeChunkComponentType<A> aChunkType;
    31.     public ArchetypeChunkComponentType<B> bChunkType;
    32.  
    33.     [ReadOnly]
    34.     [DeallocateOnJobCompletion]
    35.     public NativeArray<ArchetypeChunk> chunks;
    36.  
    37.     public void Execute(int index) {
    38.       Process(this.chunks[index]);
    39.     }
    40.    
    41.     private void Process(ArchetypeChunk chunk) {
    42.       NativeArray<A> aArr = chunk.GetNativeArray(aChunkType);
    43.       NativeArray<B> bArr = chunk.GetNativeArray(bChunkType);
    44.      
    45.       // only A || B not A & B
    46.     }
    47.  
    48.   }
    49. }
    50.  
     
  9. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
    here I must agree - skipping whole chunks is not a big deal
     
  10. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,153
    I added it to my benchmark to compare.

    While it's elegant you're main thread performance it's much much worse, and this is the worst place to add delays.

    upload_2018-11-23_14-17-15.png

    upload_2018-11-23_14-21-39.png

    upload_2018-11-23_14-20-27.png

    upload_2018-11-23_14-20-40.png

    And the job itself is slow (I suspect the inner batch count needs tweaking)

    upload_2018-11-23_14-19-24.png

    I mean the performance isn't going to really be an issue, but when you're concerned about a few quick checks on entities it's a bit weird to overlook the performance implications of the main thread..
     
    Last edited: Nov 23, 2018
  11. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
  12. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    290
    Didn't read everything but I think this didn't come up:
    You can just pass two queries to GetComponentGroup creation: A & !B and !A & B
     
  13. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,153
    That is an excellent point. I keep forgetting about that.
     
  14. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    74
    I used it a bit, really easy to read and write!
    Can there be a way to use readonly components?
     
  15. rudypoo

    rudypoo

    Joined:
    Jul 16, 2018
    Posts:
    4
    You could add some bool parameters to the Any and All. You can even make them optional if your use case is to have read/write access more often than read only access.

    E.g.,
    Code (CSharp):
    1. public QueryBuilder All<T,T2,T3>(bool isReadOnly1 = false, bool isReadOnly2 = false, bool isReadOnly3 = false)
    2.     where T:struct, IComponentData
    3.     where T2:struct, IComponentData
    4.     where T3:struct, IComponentData
    5.   {
    6.     if(isReadOnly1)
    7.        _all.Add(ComponentType.ReadOnly<T>());
    8.     else
    9.        _all.Add(ComponentType.Create<T>());
    10.  
    11.     if(isReadOnly2)
    12.        _all.Add(ComponentType.ReadOnly<T2>());
    13.     else
    14.        _all.Add(ComponentType.Create<T2>());
    15.  
    16.     if(isReadOnly3)
    17.        _all.Add(ComponentType.ReadOnly<T3>());
    18.     else
    19.        _all.Add(ComponentType.Create<T3>());
    20.  
    21.     return this;
    22.   }
     
  16. learc83

    learc83

    Joined:
    Feb 28, 2018
    Posts:
    28
    I modified SubPixelPerfect's code slightly so that I can add
    using static QueryBuilder;

    And then I can call QB directly to get a new instance of QueryBuilder without extending ComponentSystem.

    I'm using this to build a state machine as outlined in R. Fabian's Data-Oriented Design book, so I wanted a way to easily extend queries beyond ORing them. I added a Copy property which returns a copy of the QueryBuilder to facilitate this.

    I'm basically storing QueryBuilders instead of actual queries and only converting them to an actual EntityArchetypeQuery when I need to. Also to avoid having to call Build I made an implicit conversion operator that converts to EntityArchetypeQuery. I also extended the Any, All, and None methods to take 6 type params.

    Code (CSharp):
    1. public class QueryBuilder
    2. {
    3.  
    4.     private List<ComponentType> _all = new List<ComponentType>();
    5.     private List<ComponentType> _any = new List<ComponentType>();
    6.     private List<ComponentType> _none = new List<ComponentType>();
    7.  
    8.     public QueryBuilder() { }
    9.  
    10.     public QueryBuilder(List<ComponentType> all, List<ComponentType> any, List<ComponentType> none)
    11.     {
    12.         _all = all;
    13.         _any = any;
    14.         _none = none;
    15.     }
    16.  
    17.     public static QueryBuilder QB => new QueryBuilder();
    18.  
    19.     public EntityArchetypeQuery Build => new EntityArchetypeQuery()
    20.     {
    21.         All = _all.ToArray(),
    22.         None = _none.ToArray(),
    23.         Any = _any.ToArray()
    24.     };
    25.  
    26.     // Make a deep copy (shallow copy of each array, but the arrays are value types)
    27.     public QueryBuilder Copy => new QueryBuilder(_all.ToList(), _any.ToList(), _none.ToList());
    28.  
    29.     // Implicit conversion to EntityArchetypeQuery--avoids the need to call .Build
    30.     public static implicit operator EntityArchetypeQuery(QueryBuilder qb)
    31.     {
    32.         return qb.Build;
    33.     }
    34.  
    35.     public QueryBuilder All<T>()
    36.       where T : struct, IComponentData
    37.     {
    38.         _all.Add(ComponentType.Create<T>());
    39.         return this;
    40.     }
    41.     public QueryBuilder All<T, T2>()
    42.       where T : struct, IComponentData
    43.       where T2 : struct, IComponentData
    44.     {
    45.         _all.Add(ComponentType.Create<T>());
    46.         _all.Add(ComponentType.Create<T2>());
    47.         return this;
    48.     }
    49.     public QueryBuilder All<T, T2, T3>()
    50.       where T : struct, IComponentData
    51.       where T2 : struct, IComponentData
    52.       where T3 : struct, IComponentData
    53.     {
    54.         _all.Add(ComponentType.Create<T>());
    55.         _all.Add(ComponentType.Create<T2>());
    56.         _all.Add(ComponentType.Create<T3>());
    57.         return this;
    58.     }
    59.     public QueryBuilder All<T, T2, T3, T4>()
    60.       where T : struct, IComponentData
    61.       where T2 : struct, IComponentData
    62.       where T3 : struct, IComponentData
    63.       where T4 : struct, IComponentData
    64.     {
    65.         _all.Add(ComponentType.Create<T>());
    66.         _all.Add(ComponentType.Create<T2>());
    67.         _all.Add(ComponentType.Create<T3>());
    68.         _all.Add(ComponentType.Create<T4>());
    69.         return this;
    70.     }
    71.     public QueryBuilder All<T, T2, T3, T4, T5>()
    72.       where T : struct, IComponentData
    73.       where T2 : struct, IComponentData
    74.       where T3 : struct, IComponentData
    75.       where T4 : struct, IComponentData
    76.       where T5 : struct, IComponentData
    77.     {
    78.         _all.Add(ComponentType.Create<T>());
    79.         _all.Add(ComponentType.Create<T2>());
    80.         _all.Add(ComponentType.Create<T3>());
    81.         _all.Add(ComponentType.Create<T4>());
    82.         _all.Add(ComponentType.Create<T5>());
    83.         return this;
    84.     }
    85.     public QueryBuilder All<T, T2, T3, T4, T5, T6>()
    86.       where T : struct, IComponentData
    87.       where T2 : struct, IComponentData
    88.       where T3 : struct, IComponentData
    89.       where T4 : struct, IComponentData
    90.       where T5 : struct, IComponentData
    91.       where T6 : struct, IComponentData
    92.     {
    93.         _all.Add(ComponentType.Create<T>());
    94.         _all.Add(ComponentType.Create<T2>());
    95.         _all.Add(ComponentType.Create<T3>());
    96.         _all.Add(ComponentType.Create<T4>());
    97.         _all.Add(ComponentType.Create<T5>());
    98.         _all.Add(ComponentType.Create<T6>());
    99.         return this;
    100.     }
    101.  
    102.  
    103.     public QueryBuilder None<T>() where T : struct, IComponentData
    104.     {
    105.         _none.Add(ComponentType.Create<T>());
    106.         return this;
    107.     }
    108.     public QueryBuilder None<T, T2>()
    109.       where T : struct, IComponentData
    110.       where T2 : struct, IComponentData
    111.     {
    112.         _none.Add(ComponentType.Create<T>());
    113.         _none.Add(ComponentType.Create<T2>());
    114.         return this;
    115.     }
    116.     public QueryBuilder None<T, T2, T3>()
    117.       where T : struct, IComponentData
    118.       where T2 : struct, IComponentData
    119.       where T3 : struct, IComponentData
    120.     {
    121.         _none.Add(ComponentType.Create<T>());
    122.         _none.Add(ComponentType.Create<T2>());
    123.         _none.Add(ComponentType.Create<T3>());
    124.         return this;
    125.     }
    126.     public QueryBuilder None<T, T2, T3, T4>()
    127.       where T : struct, IComponentData
    128.       where T2 : struct, IComponentData
    129.       where T3 : struct, IComponentData
    130.       where T4 : struct, IComponentData
    131.     {
    132.         _none.Add(ComponentType.Create<T>());
    133.         _none.Add(ComponentType.Create<T2>());
    134.         _none.Add(ComponentType.Create<T3>());
    135.         _none.Add(ComponentType.Create<T4>());
    136.         return this;
    137.     }
    138.     public QueryBuilder None<T, T2, T3, T4, T5>()
    139.       where T : struct, IComponentData
    140.       where T2 : struct, IComponentData
    141.       where T3 : struct, IComponentData
    142.       where T4 : struct, IComponentData
    143.       where T5 : struct, IComponentData
    144.     {
    145.         _none.Add(ComponentType.Create<T>());
    146.         _none.Add(ComponentType.Create<T2>());
    147.         _none.Add(ComponentType.Create<T3>());
    148.         _none.Add(ComponentType.Create<T4>());
    149.         _none.Add(ComponentType.Create<T5>());
    150.         return this;
    151.     }
    152.     public QueryBuilder None<T, T2, T3, T4, T5, T6>()
    153.       where T : struct, IComponentData
    154.       where T2 : struct, IComponentData
    155.       where T3 : struct, IComponentData
    156.       where T4 : struct, IComponentData
    157.       where T5 : struct, IComponentData
    158.       where T6 : struct, IComponentData
    159.     {
    160.         _none.Add(ComponentType.Create<T>());
    161.         _none.Add(ComponentType.Create<T2>());
    162.         _none.Add(ComponentType.Create<T3>());
    163.         _none.Add(ComponentType.Create<T4>());
    164.         _none.Add(ComponentType.Create<T5>());
    165.         _none.Add(ComponentType.Create<T6>());
    166.         return this;
    167.     }
    168.  
    169.  
    170.  
    171.     public QueryBuilder Any<T>() where T : struct, IComponentData
    172.     {
    173.         _any.Add(ComponentType.Create<T>());
    174.         return this;
    175.     }
    176.     public QueryBuilder Any<T, T2>()
    177.       where T : struct, IComponentData
    178.       where T2 : struct, IComponentData
    179.     {
    180.         _any.Add(ComponentType.Create<T>());
    181.         _any.Add(ComponentType.Create<T2>());
    182.         return this;
    183.     }
    184.     public QueryBuilder Any<T, T2, T3>()
    185.       where T : struct, IComponentData
    186.       where T2 : struct, IComponentData
    187.       where T3 : struct, IComponentData
    188.     {
    189.         _any.Add(ComponentType.Create<T>());
    190.         _any.Add(ComponentType.Create<T2>());
    191.         _any.Add(ComponentType.Create<T3>());
    192.         return this;
    193.     }
    194.     public QueryBuilder Any<T, T2, T3, T4>()
    195.       where T : struct, IComponentData
    196.       where T2 : struct, IComponentData
    197.       where T3 : struct, IComponentData
    198.       where T4 : struct, IComponentData
    199.     {
    200.         _any.Add(ComponentType.Create<T>());
    201.         _any.Add(ComponentType.Create<T2>());
    202.         _any.Add(ComponentType.Create<T3>());
    203.         _any.Add(ComponentType.Create<T4>());
    204.         return this;
    205.     }
    206.     public QueryBuilder Any<T, T2, T3, T4, T5>()
    207.       where T : struct, IComponentData
    208.       where T2 : struct, IComponentData
    209.       where T3 : struct, IComponentData
    210.       where T4 : struct, IComponentData
    211.       where T5 : struct, IComponentData
    212.     {
    213.         _any.Add(ComponentType.Create<T>());
    214.         _any.Add(ComponentType.Create<T2>());
    215.         _any.Add(ComponentType.Create<T3>());
    216.         _any.Add(ComponentType.Create<T4>());
    217.         _any.Add(ComponentType.Create<T5>());
    218.         return this;
    219.     }
    220.     public QueryBuilder Any<T, T2, T3, T4, T5, T6>()
    221.       where T : struct, IComponentData
    222.       where T2 : struct, IComponentData
    223.       where T3 : struct, IComponentData
    224.       where T4 : struct, IComponentData
    225.       where T5 : struct, IComponentData
    226.       where T6 : struct, IComponentData
    227.     {
    228.         _any.Add(ComponentType.Create<T>());
    229.         _any.Add(ComponentType.Create<T2>());
    230.         _any.Add(ComponentType.Create<T3>());
    231.         _any.Add(ComponentType.Create<T4>());
    232.         _any.Add(ComponentType.Create<T5>());
    233.         _any.Add(ComponentType.Create<T6>());
    234.         return this;
    235.     }
    236. }
    237.  
     
    rudypoo and SubPixelPerfect like this.
  17. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    166
    I love those improvements :)

     
    rudypoo and learc83 like this.
  18. learc83

    learc83

    Joined:
    Feb 28, 2018
    Posts:
    28
    I decided to extend QueryBuilder to include the read/write params that rudypoo suggested because I found I needed that functionality.

    Instead of writing it all by hand I extracted it out in a T4 template. Now there is one param at the top called upto that will let you set the maximum number of type params.

    If you haven't used T4 before, just make a file called QueryBuilder.tt and VS will automatically make a generated sibling file called QueryBuilder.cs.

    The T4 control statements aren't indented so that it doesn't mess up the .cs file indentation.

    Code (CSharp):
    1. <#@ template debug="false" hostspecific="false" language="C#" #>
    2. <#@ assembly name="System.Core" #>
    3. <#@ import namespace="System.Linq" #>
    4. <#@ import namespace="System.Text" #>
    5. <#@ import namespace="System.Collections.Generic" #>
    6. <#@ output extension=".cs" #>
    7. <# var allAnyNone = new List<String>() {"all", "any", "none"};#>
    8. <# int upto = 6;#>
    9. <#
    10.     List<String> totalTypeVars  = new List<String>();
    11.     for (int i = 0; i < upto; i++)
    12.     {
    13.         totalTypeVars.Add("T" + i);  
    14.     }
    15. #>
    16. using System;
    17. using System.Collections.Generic;
    18. using System.Linq;
    19. using Unity.Collections;
    20. using Unity.Entities;
    21.  
    22. public class QueryBuilder
    23. {
    24.     private List<ComponentType> _all = new List<ComponentType>();
    25.     private List<ComponentType> _any = new List<ComponentType>();
    26.     private List<ComponentType> _none = new List<ComponentType>();
    27.  
    28.     public QueryBuilder() { }
    29.  
    30.     public QueryBuilder(List<ComponentType> all, List<ComponentType> any, List<ComponentType> none)
    31.     {
    32.         _all = all;
    33.         _any = any;
    34.         _none = none;
    35.     }
    36.  
    37.     public static QueryBuilder QB => new QueryBuilder();
    38.  
    39.     public EntityArchetypeQuery Build => new EntityArchetypeQuery()
    40.     {
    41.         All = _all.ToArray(),
    42.         None = _none.ToArray(),
    43.         Any = _any.ToArray()
    44.     };
    45.  
    46.     // Make a deep copy (shallow copy of each array, but the arrays are value types)
    47.     public QueryBuilder Copy => new QueryBuilder(_all.ToList(), _any.ToList(), _none.ToList());
    48.  
    49.     // Implicit conversion to EntityArchetypeQuery--avoids the need to call .Build
    50.     public static implicit operator EntityArchetypeQuery(QueryBuilder qb)
    51.     {
    52.         return qb.Build;
    53.     }
    54. <# foreach(var name in allAnyNone) { #>
    55. <# for (int i = 1; i <= totalTypeVars.Count; i++) { #>
    56. <# var arrayNames = new List<String>(); #>
    57. <# var typeVars = totalTypeVars.GetRange(0,i); #>
    58. <# var rwVarNames = typeVars.Select( x => "readOnly" + x); #>
    59. <# var rwVarDecl = rwVarNames.Select( x => "bool " + x + "=true"); #>
    60. <# var typeString = String.Join(",", typeVars); #>
    61.  
    62.     public QueryBuilder <#= char.ToUpper(name[0]) + name.Substring(1) #><<#= typeString #>>(<#= String.Join(", ", rwVarDecl) #>)
    63. <# foreach(var t in typeVars) { #>
    64.         where <#= t #> : struct, IComponentData
    65. <#}#>
    66.     {
    67. <# var rwVarIndex = 0; #>
    68. <# foreach(var t in typeVars) { #>
    69.         if (<#= rwVarNames.ElementAt(rwVarIndex++) #>)
    70.             _<#= name #>.Add(ComponentType.ReadOnly<<#= t #>>());
    71.         else
    72.             _<#= name #>.Add(ComponentType.Create<<#= t #>>());
    73.  
    74. <#}#>
    75.         return this;
    76.     }
    77. <#}#>
    78. <#}#>
    79. }
    80.  
     
    rudypoo likes this.
  19. SergeyRomanko

    SergeyRomanko

    Joined:
    Oct 18, 2014
    Posts:
    6
    Hi @learc83 , I took your QueryBuilder, made some modifications on it and came up with approach without core generation. I want to share the result with you.

    Here is example of my syntax

    Code (CSharp):
    1. using static Romanko.ComponentTypeUtility;
    2.  
    3. public class StockViewSystem : JobComponentSystem {
    4.        
    5.     private ComponentGroup inventoryQuery;
    6.     protected override void OnCreateManager() {
    7.         inventoryQuery = All(RO<SelectionMarker>(), RO<Stock>(), RO<InventoryBuffer>()).Build(this);
    8.     }
    9.     protected override JobHandle OnUpdate(JobHandle inputDeps) { ... }
    10. }
    And here is the code itself

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Reflection;
    5. using Unity.Entities;
    6.  
    7. // ReSharper disable All
    8.  
    9. namespace Romanko {
    10.    
    11.     public class ComponentTypeUtility {
    12.        
    13.         public static ComponentType RO<T>() => ComponentType.ReadOnly<T>();
    14.         public static ComponentType RW<T>() => ComponentType.Create<T>();
    15.        
    16.         public static QueryBuilder All(params ComponentType[] types) {
    17.             return new QueryBuilder(types.ToList(), null, null);
    18.         }      
    19.        
    20.         public static QueryBuilder Any(params ComponentType[] types) {
    21.             return new QueryBuilder(null, types.ToList(), null);
    22.         }  
    23.        
    24.         public static QueryBuilder None(params ComponentType[] types) {
    25.             return new QueryBuilder(null, null, types.ToList());
    26.         }
    27.        
    28.     }
    29.  
    30.     public class QueryBuilder {
    31.  
    32.         private static MethodInfo methodInfo;
    33.         private static object[] methodParameters = new object[1];
    34.         private static EntityArchetypeQuery[] queries = new EntityArchetypeQuery[1];
    35.        
    36.         private List<ComponentType> _all;
    37.         private List<ComponentType> _any;
    38.         private List<ComponentType> _none;
    39.        
    40.         private List<ComponentType> _All  => (_all != null) ? _all : (_all = new List<ComponentType>());
    41.         private List<ComponentType> _Any  => (_any != null) ? _any : (_any = new List<ComponentType>());
    42.         private List<ComponentType> _None => (_none != null) ? _none : (_none = new List<ComponentType>());
    43.  
    44.         public QueryBuilder() { }
    45.  
    46.         public QueryBuilder(List<ComponentType> all, List<ComponentType> any, List<ComponentType> none) {
    47.             _all = all; _any = any; _none = none;
    48.         }
    49.  
    50.         public QueryBuilder All(params ComponentType[] types) => AddComponentTypesToList(_All, types);
    51.         public QueryBuilder Any(params ComponentType[] types) => AddComponentTypesToList(_Any, types);
    52.         public QueryBuilder None(params ComponentType[] types) => AddComponentTypesToList(_None, types);
    53.  
    54.         private QueryBuilder AddComponentTypesToList(List<ComponentType> list, ComponentType[] types) {
    55.             foreach (var type in types) list.Add(type);
    56.             return this;
    57.         }
    58.        
    59.         // Implicit conversion to EntityArchetypeQuery--avoids the need to call .Build
    60.         public static implicit operator EntityArchetypeQuery(QueryBuilder qb) {
    61.             EntityArchetypeQuery result = new EntityArchetypeQuery();
    62.             if (qb._all != null) result.All = qb._all.ToArray();
    63.             if (qb._none != null) result.None = qb._none.ToArray();
    64.             if (qb._any != null) result.Any = qb._any.ToArray();
    65.             return result;
    66.         }
    67.  
    68.         public ComponentGroup Build(ComponentSystemBase system) {
    69.             if (methodInfo == null) {
    70.                 methodParameters[0] = queries;
    71.  
    72.                 methodInfo = typeof(ComponentSystemBase)
    73.                     .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
    74.                     .FirstOrDefault(x => x.Name == "GetComponentGroup"
    75.                                          && x.GetParameters()[0].ParameterType == typeof(EntityArchetypeQuery[]));
    76.  
    77.                 if (methodInfo == null) throw new NullReferenceException("GetComponentGroup method moved or changed");
    78.             }
    79.  
    80.             queries[0] = (EntityArchetypeQuery) this;
    81.             return (ComponentGroup) methodInfo.Invoke(system, methodParameters);
    82.         }
    83.  
    84.     }
    85.  
    86. }
     
    SubPixelPerfect and learc83 like this.