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

Collections within IComponent Data?

Discussion in 'Entity Component System' started by Drezta, Jan 8, 2020.

  1. Drezta

    Drezta

    Joined:
    Nov 4, 2012
    Posts:
    8
    Like a few others I want to explore the idea of cell dynamics within DOTS.

    I am trying to create at runtime a list of neighbors per cell for later analysis.

    The neighboring entities should live in a Dynamic buffer as I wanted an iteratable collection and had been lead to believe Lists, Dictionaries and their native counterparts are unavailable on entities.

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Mathematics;
    3.  
    4. [GenerateAuthoringComponent]
    5. public struct NeighborhoodData : IComponentData
    6. {
    7.     public DynamicBuffer<EntityAddresseBuffer> neighborhood;
    8. }
    9.  
    10.  
    11. public struct EntityAddresseBuffer : IBufferElementData
    12. {
    13.     public int3 address; //-1,-1,-1 bottom, left, back  - 1,1,1 top, right, front
    14.     public Entity entity;
    15. }
    16.  
    17.  
    18.  
    The following should, when the cell spawns, raycast the 8 2d nearest neighbor directions and then add any hits to the dynamic buffer.

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Jobs;
    3. using Unity.Transforms;
    4. using Unity.Physics;
    5. using Unity.Mathematics;
    6. using Unity.Collections;
    7.  
    8.  
    9. [AlwaysSynchronizeSystem]
    10. public class BlockSystem : JobComponentSystem
    11. {
    12.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    13.     {
    14.         return default;
    15.     }
    16.  
    17.     protected override void OnStartRunning()
    18.     {
    19.         base.OnStartRunning();
    20.         NativeArray<int3> neighborhoodAddresses = new NativeArray<int3>(new int3[]
    21.                                     {
    22.                                         new int3(-1, -1, 0), new int3(0, -1, 0), new int3(1, -1, 0),
    23.                                         new int3(-1, 0, 0),                      new int3(1, 0, 0),
    24.                                         new int3(-1, 1, 0), new int3(0, 1, 0), new int3(1, 1, 0),
    25.  
    26.                                     }, Allocator.Temp);
    27.         Entities.WithoutBurst().ForEach
    28.             (
    29.             (ref NeighborhoodData neighborhood, ref Translation translation) =>
    30.             {
    31.                 foreach(int3 address in neighborhoodAddresses)
    32.                 {
    33.                     float3 dir = translation.Value + new float3(address);
    34.                     Entity hit = Raycast(translation.Value, dir);
    35.                     EntityAddresseBuffer eab = new EntityAddresseBuffer();
    36.                     eab.address = address;
    37.                     eab.entity = hit;
    38.                     neighborhood.neighborhood.Add(eab);
    39.                 }
    40.             }
    41.             ).Run();
    42.  
    43.         neighborhoodAddresses.Dispose();
    44.     }
    45.  
    46.     //mostly Copied from the DOTS physics raycast docs
    47.     public Entity Raycast(float3 RayFrom, float3 RayTo)
    48.     {
    49.         var physicsWorldSystem = GameManager.main.physicsWorld;
    50.         var collisionWorld = physicsWorldSystem.PhysicsWorld.CollisionWorld;
    51.  
    52.         RaycastInput input = new RaycastInput()
    53.         {
    54.             Start = RayFrom,
    55.             End = RayTo,
    56.             Filter = new CollisionFilter()
    57.             {
    58.                 BelongsTo = ~0u,
    59.                 CollidesWith = ~0u, // all 1s, so all layers, collide with everything
    60.                 GroupIndex = 0
    61.             }
    62.         };
    63.  
    64.         RaycastHit hit = new RaycastHit();
    65.         bool haveHit = collisionWorld.CastRay(input, out hit);
    66.         if (haveHit)
    67.         {
    68.             // see hit.Position
    69.             // see hit.SurfaceNormal
    70.             Entity e = physicsWorldSystem.PhysicsWorld.Bodies[hit.RigidBodyIndex].Entity;
    71.             return e;
    72.         }
    73.         return Entity.Null;
    74.     }
    75. }
    76.  
    but I get the following error:
    InvalidOperationException: NeighborhoodData used in NativeArray<NeighborhoodData> must be unmanaged (contain no managed types) and cannot itself be a native container type.
    Unity.Collections.NativeArray`1[T].IsUnmanagedAndThrow () (at <0d01204b437e47c1a78b600b597a89f4>:0)

    Obviously I need to remove the managed type from this script, but am unsure what part it could be other than the DynamicBuffer and can't think of an alternative that wouldn't just be hard coding each neighbor.
     
    Last edited: Jan 10, 2020
  2. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    DynamicBuffer is a handler for main thread access. In the editor, it has managed safety check objects embedded in it. That said, IBufferElementData is special, you shouldn't put it inside another component, as the Dynamic Buffer elements won't actually be handled by ECS at all.

    You just need the Entity Address Buffer type. That being said, I don't know if buffer types are compatible with
    [GenerateAuthoringComponent]
    . You may want to look into doing it manually with
    IConvertGameObjectToEntity
    on a custom MonoBehaviour.
     
  3. Drezta

    Drezta

    Joined:
    Nov 4, 2012
    Posts:
    8
    Thanks Recursive, I'm guesing that means no collections as IComponentData fields/properties.
     
  4. Deleted User

    Deleted User

    Guest

    You may store its pointer in a Component and then convert it back to a Buffer if needed, but you are fully responsible for its safety.