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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Sharing Data between Entities

Discussion in 'Entity Component System' started by Vacummus, Nov 5, 2018.

  1. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I am creating an organism that compromises of multiple entities with each entity having a rigidbody component. When the AI systems want to move this organism, they have to apply force towards a desired direction for every single rigidbody component. What would be a good way to share the desired direction between all these entities?

    Currently, I have a MovementInput: IComponentData { public float2 direction }. This component gets updated by the FollowPathSystem, and it gets used by the RigidbodySystem to apply force towards the desired direction. So if an organism consists of 8 entities (with Rigidbodies), then each entity must also have a MovementInput component. But the problem here is that there is data duplication since all 8 entities have the same MovementInput direction data. I thought about using ISharedComponentData for this, but that's not a good idea since the MovementInput component changes often.
     
    Last edited: Nov 5, 2018
  2. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    I think that the right approach here is to create a separate entity for whole Organism, this organism entity may have no renderable parts, but it should contain a list (DynamicBuffer) of all it's parts. OrganismMovement system should iterate over all body parts and move them way you need it
     
  3. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Put the MovementComponent on the root entity for that collection of entities.

    Then Each thing that does something, looks up the MovementInput via ComponentDataFromEntity<MovementEntity> and an Entity InputRef; on the thing.

    @SubPixelPerfect This approach is recommended because it scales better. MovementEntity in such a scenario is read only, thus the different entities consuming the data can be parallelized. While it may not matter for 8 things, its generally cleaner and a better default.
     
    Enzi and Vacummus like this.
  4. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I like this. Though is performance something to be concerned about with this approach since the use of ComponentDataFromEntity will result in a cache miss? Not a big deal for 8 rigidbodies of course. But if I am trying process the rigidbody force for 200 organisms (1600 rigidbodies since each organism has 8 rigidbodies), it seems like my cache will get invalidated for every use of ComponentDataFromEntity<MovementEntity>.
     
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    It's not a synch point it's just lookup and it not invalidate chunks
     
    Vacummus likes this.
  6. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    I am pretty sure ComponentDataFromEntity lookups will not show up at that scale. Sure when its easy to avoid and do fully linear access thats better and a good default, but there are plenty situations where thats just not the setup you have...
     
    Sylmerria and Vacummus like this.
  7. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    Ahh! I think I am getting it. If I have the following 8 entities that compromise an organism:
    Entity1: MovementInput, Rigidbody, OrganismHeadEntity
    Entity2-8: Rigidbody, OrganismHeadEntity

    Where OrganismHeadEntity has "public Entity headRef;" that points to the head entity of the organism (Entity1). Then doing ComponentDataFromEntity<MovementInput> will NOT result in cache misses since the component data of all 8 entities easily fits within a cache line. This is brilliant. You guys/gals rock!!
     
    Last edited: Nov 6, 2018
  8. LegendaryTale

    LegendaryTale

    Joined:
    Oct 4, 2018
    Posts:
    2
    I have a Component explicitly responsible for allowing the data of one Entity to be referenced by another:
    Code (CSharp):
    1.     /// <summary>
    2.     /// Defines a Reference Component, which is a Component that represents a reference
    3.     /// to a Component (of type T) on the ReferencedEntity.
    4.     /// </summary>
    5.     /// <typeparam name="T">The Type of Component being referenced.</typeparam>
    6.     public struct Reference<T> : IComponentData where T : struct, IComponentData
    7.     {
    8.         /// <summary>
    9.         /// The referenced Entity.
    10.         /// </summary>
    11.         public Entity ReferencedEntity { private get; set; }
    12.  
    13.         /// <summary>
    14.         /// The default T value of this Reference, provided if there is no ReferencedEntity.
    15.         /// </summary>
    16.         private readonly T Default;
    17.  
    18.  
    19.         /// <summary>
    20.         /// Provides the referenced Component.
    21.         /// This method is not safe for use with parallel jobs.
    22.         /// </summary>
    23.         /// <returns>The referenced Component.</returns>
    24.         public T GetReferenced()
    25.         {
    26.             EntityManager manager = World.Active.GetOrCreateManager<EntityManager>();
    27.  
    28.             if (manager.HasComponent<T>(ReferencedEntity))
    29.                 return manager.GetComponentData<T>(ReferencedEntity);
    30.             return Default;
    31.         }
    32.  
    33.         /// <summary>
    34.         /// Provides the referenced Component.
    35.         /// This method is safe for use with parallel jobs.
    36.         /// </summary>
    37.         /// <param name="fromEntity">The ComponentDataFromEntity required to access the
    38.         /// referenced Component during parallel job execution.</param>
    39.         /// <returns>The referenced Component.</returns>
    40.         public T GetReferenced(ComponentDataFromEntity<T> fromEntity)
    41.         {
    42.             if (fromEntity.Exists(ReferencedEntity))
    43.                 return fromEntity[ReferencedEntity];
    44.             return Default;
    45.         }
    46.     }
    The generic declaration lets me use specific cases (like Reference<MovementInput> perhaps) in Systems just as I would any other Components, so I can keep those Systems focused on the functionality provided by referencing, and less so on any specific case of referencing.

    Depending on your exact implementation, a Component like this may give you some flexibility in developing your Systems.
     
    Orimay likes this.