Search Unity

keep reference to entities in a entity

Discussion in 'Entity Component System' started by yc960, Jun 17, 2018.

  1. yc960

    yc960

    Joined:
    Apr 30, 2015
    Posts:
    228
    My entity needs to hold references to multiple entities like a hierarchy structure. I've tried to attach fixed array but it shows "ArgumentException: Entity[3] is not a valid component type."

    Code (CSharp):
    1. entityManager.AddComponent(entity, ComponentType.FixedArray(typeof(Entity),3));
    What is the intended method to keep reference to multiple entity references?
     
    PhilSA likes this.
  2. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I haven't yet tried doing this myself, but I'm very interested in knowing this too

    For example how to store a fixed size array in an IComponentData, and also what happens if multiple FixedArrays are added the way you just showed. How are they identified individually and retrieved later on?
     
    Last edited: Jun 17, 2018
  3. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    If i remember correctly, you can only add a fixed array to an entity when you create the entity. Once the entity has been created, you cannot call AddComponent to add a fixed array.

    I'm not sure what your specific use case is, but another approach may be to store the parent entity in the child using either a component or shared component (like Unity.Transforms.TransformParent).

    I have taken to encapsulating the array item type in a unique struct to easily identify it. e.g...
    Code (CSharp):
    1. public struct Height
    2. {
    3.     public int Value;
    4. }
    5.  
    6. entityManager.CreateEntity(ComponentType.FixedArray(typeof(Height), numElements));
     
  4. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    An entity is a reference to a specific index in a ComponentGroup. So even if you stored an entity, it would only be valid for that ComponentGroup until the next time you create/destroy an entity. At which time the entity would point to another entity or an invalid index.

    My understanding of FixedArray is that it's a tool to create chunks. It say's how many components of a specific type are in a chunk. So the code above would create numElements of entities all with a single Height component in a single chunk.

    Hierarchies at the entity level just don't fit the flow and goals of ECS. ECS has a strong focus on pairing logic with data in a way that focuses on doing the max amount of work you can per piece of data. So a typical hierarchy would generally pull in a huge amount of data, the parent and all it's children, just to work on a small part of that data. Which at the cpu level would translate into pulling in many cache lines worth of data just to be able to work on one or two.

    So it's not so much about how to fit hierarchies into ECS. You kind of need to start from the ground up and re-imagine the logic flow as well as the data strucures. Ie you can't just take an existing hierarchical structure and cram it into ECS. Well you probably could with some convoluted design but you wouldn't want to.
     
  5. JPrzemieniecki

    JPrzemieniecki

    Joined:
    Feb 7, 2013
    Posts:
    33
    An entity is persistent, and stays valid across ComponentGroup invalidations. It is not an index into a ComponentGroup (Implementation-wise, it is an index into an internal array in EntityDataManager, plus a Version number to prevent accessing a reused index).

    @yc960 You can wrap an Entity in a struct, and add a FixedArray of wrappers. Whether it is the "intended method" I don't know.

    Code (CSharp):
    1. struct  EntityRef
    2. {
    3.     public Entity Value;
    4. }
    5.  
    6. entityManager.AddComponent(entity, ComponentType.FixedArray(typeof(EntityRef), 3));
     
  6. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Ah ok I see so they use a pool. So it would be stable within the same ComponentGroup to a point. If the entity gets destroyed eventually it's index will likely get reassigned. At which point you would have a dangling reference.
     
  7. BrianWill

    BrianWill

    Joined:
    Oct 10, 2014
    Posts:
    38
    @JPrzemieniecki This really feels like a bug. Looking at the code, it seems FixedArray types must be classified as TypeCategory.OtherValueType, which Entity is not, but I don't see why they shouldn't be allowed. If there is a reason not to allow them, why isn't the restriction enforced recursively for the members of structs?
     
    JPrzemieniecki likes this.
  8. BrianWill

    BrianWill

    Joined:
    Oct 10, 2014
    Posts:
    38
    @snacktime The EntityManager's chunks store the entity and component data, and the ComponentGroup iterators just point into these chunks. Adding/removing/moving entities and adding/removing components dangerously interfere with any ComponentGroup iterators created before the change. That's the main reason we need EntityCommandBuffers: to delay these ops until after we're done iterating.

    When an entity is destroyed, it's removed from its chunk and the version number associated with its index in the EntityManager is incremented, so when you look up entities through the EntityManager, it knows if your Entity id is still a valid reference: if the Entity id you pass in has a version less than the version associated with the index in the EntityManager, it knows you're trying to look up a dead entity. (This is true whether or not the index has yet been reused for a new entity.)
     
    Ronsu900 and KwahuNashoba like this.
  9. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Funny I found this after I start implementing, and figuring out, how to do it ;)
    But yes, this is pretty way of doing it. And works form as well, while still testing system behaviors.

    Only problem I have, since fixed array has always same length, how to check the current length of array, if entities has been removed? Is the checking version a best options?
     
  10. dthurn

    dthurn

    Joined:
    Feb 17, 2015
    Posts:
    77
    I'd also like to know the best practices here -- currently looking at just storing child entities in a NativeList. I know tree structures have worse performance, but there are a lot of things that are just naturally hierarchical.
     
  11. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Regarding tree structures, it depends how you design them.
    So far, I just have entities, which using structs as components, with fixed array, to store references to relevant entities.
    Then these entities, can store references to other entities and so on.

    So by design is a tree structure (in this case for octrees). But physical architecture of entities, stays as per ECS. So you don't move entities, like objects in OOP. So far how I understand, is all bout filtering right groups.
     
  12. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,705
    Do you have a simple example of this , I’m trying to find the best way to store a reference of neighboring entities on current entity. Spinning on this for days ... it has brought me to my knees.
     
  13. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Since you have replied to quite old post and since then, lots of thing changed, like Fixed Array is gone for example.
    But if you know fixed number of entities neighbors per entity, like cube has 6 neighbors, then you can have component with 6 entities neighbors properties (left, right, up, down, front, back).
    Or use dynamic buffer per entity, storing its relations to other entities.
    Also you could use MultiHashMaps as well.
    Or even NativeArrays, but that become more complex matter.
     
  14. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,705
    ok since I know I will only ever have 3 neighbors I think adding entities should work. I just keep getting bit down the line no matter what direction I go.
     
  15. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,705
    if i keep an neighbor entity how do I GetComponentDataFromEntity from neighbor entity inside my Entities.ForEach()
    JobComponentSystem. Is GetComponentDataFromEntity supported inside lambdas
     
  16. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Personally I don't use lambda approach.
    I stay on using default job definitions, using foreach (with entity when requires), or in some cases even chunk jobs. They may require bit more boiler plates, but at least I got desired flexibility. So probably you need similar approach.
     
  17. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    You do
    var cdfe = GetComponentDataFromEntity<Type>();
    and pass the local variable into your job. You can safely read from it in a ForEach if you pass it WithReadOnly, but you can't write to a cdfe in a JobComponentSystem ForEach unless you disable the safety system on the cdfe since it could potentially cause a race condition.

    Based on your use case it sounds like you need to force your job to run in a single thread in order to guarantee safety when using ComponentDataFromEntity. You either have to write a ForEach struct and call it with ScheduleSingle, or you can use the new SystemBase which runs a single threaded job by default with
    Schedule()
    .