Search Unity

How to think about setting up large number of entities

Discussion in 'Entity Component System' started by timmehhhhhhh, Dec 7, 2018.

  1. timmehhhhhhh

    timmehhhhhhh

    Joined:
    Sep 10, 2013
    Posts:
    157
    I've been using ECS frameworks for a while yet but am struggling a bit getting started with the Unity system - It's great, but there are many solutions to the problem ;)

    I have an entity and want to give it a component that represents some kind of Hash (ie, fixed number of bytes and characters A-Z), and another component that references that Hash. I've ended up with something like this:

    Code (CSharp):
    1.  
    2. public struct Hash : IBufferElementData
    3. {
    4.     public byte Value;
    5. }
    6.  
    7. public struct SomeParentHash : IBufferElementData
    8. {
    9.     public byte Value;
    10. }
    11.  
    Assigning SomeParentHash can potentially be a costly operation as I may have to cycle through a large number of entities to find a good match. What's the "correct" way to think about this? At first I thought a reactive system, then IJobProcessComponentData (but these are IBufferElementDatas, not IComponentDatas)... etc etc... I'm lost in a sea of options. Can I somehow ditch the IBufferElementDatas and use IComponentDatas with fixed arrays without having to sprinkle `unsafe`everywhere? Or continue with one of these strategies? Or is there some other path I'm not recognizing? I feel like I'm barely scratching the surface here.
     
  2. meanmonkey

    meanmonkey

    Joined:
    Nov 13, 2014
    Posts:
    148
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    You can use IJobProcessComponentData\IJobProcessComponentDataWithEntity with BufferFromEntity<YourBufferType> for get buffer from entity and you can use BufferFromEntity.Exists(Entity) for check, exist buffer on entity or not, also, if you want iterate only entities with YourBufferType, you can use [RequireComponentTag(typeof(YourBufferType))]
     
    meanmonkey likes this.
  4. timmehhhhhhh

    timmehhhhhhh

    Joined:
    Sep 10, 2013
    Posts:
    157
    i've found their posts very helpful ;)

    this is basically what i ended up on, it just feels a bit clunky to me, like creating an empty IComponentData just to tag these with, and not even consuming it in Execute(). it would be nice to have at least IJobProcessBufferElementData and IJobProcessBufferElementDataWithEntity, or ideally some way to mix the two (or way to stuff some kind of fixed array structure into an IComponentData).

    Code (CSharp):
    1.  
    2.         //[BurstCompile]
    3.         [RequireComponentTag(typeof(BufferA))]
    4.         [RequireComponentTag(typeof(BufferB))]
    5.         [RequireComponentTag(typeof(BufferC))]
    6.         struct Job : IJobProcessComponentDataWithEntity<SomeTag>
    7.         {
    8.             [ReadOnly] public BufferFromEntity<BufferA> bufferA;
    9.             [ReadOnly] public BufferFromEntity<BufferB> bufferB;
    10.             [ReadOnly] public BufferFromEntity<BufferC> bufferC;
    11.  
    12.             public void Execute(Entity entity, int index, ref SomeTag data)
    13.             {
    14.                 var someBytes = Encoding.UTF8.GetBytes("HELLO");
    15.                 var someArray = new BufferA[someBytes.Length];
    16.                 for (var i = 0; i < someBytes.Length; i++)
    17.                 {
    18.                     someArray[i].Value = someBytes[i];
    19.                 }
    20.                 bufferA[entity].CopyFrom(someArray);
    21.             }
    22.         }
    23.  
    24.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    25.         {
    26.             var job = new Job()
    27.             {
    28.                 bufferA = GetBufferFromEntity<BufferA>(false)
    29.                 bufferB = GetBufferFromEntity<BufferB>(false)
    30.                 bufferC = GetBufferFromEntity<BufferC>(false)
    31.             };
    32.             return job.Schedule(this, inputDeps);
    33.         }
    34.  
    Does this look "about right"? Also, I'm curious why this doesn't seem to work with Burst. Cheers!
     
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    You can't use static fields\properties inside Burst. In your case is Encoding.UTF8 <- it's static property
     
  6. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    To add to what @eizenhorn said, you could store the bytes in a NativeArray<byte> and pass that into the job. Not only will this not access a static property reference, if you mark the passed array as [ReadOnly] then this is a net performance benefit as all jobs can safely read from it.

    You also cannot make a managed array
    new BufferA[someBytes.Length]
    in a job. But with BufferFromEntity you can use it like a NativeList<T>:
    Code (CSharp):
    1. [ReadOnly] public BufferFromEntity<BufferA> bufferAs;
    2. [ReadOnly] public NativeArray<byte> SomeBytes;
    3.  
    4. public void Execute(Entity entity, int index, [ReadOnly] ref SomeTag data)
    5. {
    6.     // if you know the type has the desired dynamic buffer, you don't need to use the Exists(entity) check on the BufferFromEntity<>
    7.     var buffer = bufferAs[entity];
    8.     buffer.Clear();
    9.     buffer.ReInterpret<byte>().AddRange(someBytes); // sizes must match but is otherwise a plain old reinterpret_cast. Use wisely.
    10. }
    It can also be "shadow-copied" and masquarade as a normal array if you are not simultaneously changing it's size via ToArray();
     
  7. timmehhhhhhh

    timmehhhhhhh

    Joined:
    Sep 10, 2013
    Posts:
    157
    informative, thanks! though i was doing
    new BufferA[someBytes.Length]
    in a job...

    the issue with statics now also clarifies why i was unable to use entity command buffer. much learning still to do.
     
  8. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    You can use ECB in bursted job, but only Destroy operation.
     
  9. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    I'll add that ECB RemoveComponent works with burst, but only the overload that takes a ComponentType struct and not the generics version (since that calls into a static type lookup manager).
     
    timmehhhhhhh likes this.