Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How have you stored archetype references?

Discussion in 'Entity Component System' started by orionburcham, Jan 2, 2019.

  1. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Hi!

    So, EntityManager.CreateArchetype() returns an EntityArchetype.

    EntityManager.CreateEntity() can take an EntityArchetype argument. Because of the nature of ECS, one can assume that CreateEntity() will often be called from Systems.

    So my question is: How should a System get access to the EntityArchetype they need to pass into CreateEntity()?

    How do you personally solve this? Are you storing your EntityArchetypes in a collection somewhere, which can be accessed by Systems when they need an archetype variable?

    Please share your approach if you'd like, and thanks!
     
    Last edited: Jan 2, 2019
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    Just stored as a variable in the system that creates it.

    Even if you call CreateArchetype or CreatEntity(type[]) Entitymanager first looks for a cached version of the archetype before creating a new one.
     
    orionburcham likes this.
  3. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    What if you need that archetype in another system? Did you never want to make a database of them?

    Yeah, but then you have to pass in that long list of components every time. That’s like building a gameobject up from scratch in the before days, instead of using prefabs.

    Maybe I’m thinking about this wrong- is that what you’ve been doing every time you create an entity (passing in a set of component types)?
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    I personally consider this bad design and avoid it where possible.

    If on the rare occasion more than 1 system needs to create an identical archetype, then I'll use a factory that is shared between them.
     
    Last edited: Jan 3, 2019
    orionburcham likes this.
  5. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    I believe I understand why you think this, and I respect it. Would you mind going into more detail about why you consider that bad design?

    Am I correct to assume you'd prefer the following model: instead of multiple systems creating the same entity type, those systems would add a component tag to an entity. That tag would then be picked up by a later system, which would actually create the entity in question, and remove the tag.
     
    Last edited: Jan 3, 2019
  6. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,753
    @tertle is most likely refer to Single Responsibility Principle.
    For which is expected, there is only one place in whole program, that creates entity with given archetype.
     
    orionburcham likes this.
  7. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    In the past (and in other ECS engines) I’ve seen that ‘one place’ be Utility functions. Those could take the form of a factory, or just be explicit, individual functions to create each entity type. Multiple systems could call these Utility functions as needed to create a new entity.

    I’ve never heard of the single responsibility principle being interpreted to mean that only one system should ever ‘have a need’ to create a given entity type. I’m very interested in the idea, and how tertle may have worked it into his system order.

    There’s a real-world case I’m curios to see how it handles, involving a need to spawn multiple entities at once which work together to function as one contextual “thing” in the project.

    I hope none of this is coming off as argumentative or debating, as I’m genuinely curious of your design here. :D
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    I'm a bit busy and I'll write a bit more later, but pretty much I've found a lot of issues arise with the deferred nature of EntityCommandBuffer if you create and destroy entities or components from different systems.

    These issues often pop up at a later, unexpected time when you add a new system and the order of your systems change, so I've found a lot of pain can be avoided by just not doing it.
     
    wmpunk, orionburcham and Antypodish like this.
  9. Floofloof

    Floofloof

    Joined:
    Nov 21, 2016
    Posts:
    41
    I had this same question here and it was tertle that helped me out as well lol
    What ive been doing is just creating a static class with a bunch of ComponentType[] that can be used in CreateEntity(). ECS will try to find that archetype if it already exists and provide it to you. You dont want to store EntityArchetypes as they are specific to each EntityManager. Its better to just always refer back to the array of ComponentTypes and let the EM do the heavy lifting on CreateManager.
     
    orionburcham likes this.
  10. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    That looks great. Two questions, if you have time:

    1. This looks like you might be accessing static data from inside Jobs. While there is no explicit check against this, Unity has stated in multiple places that they strongly advise against doing it.

    I've been therefore avoiding it, even if cases where it seemed logically fine (like reading static readonly data). Hard to imagine that causing race conditions, but I'm just trying to follow Unity's guidelines- maybe they know something I don't.

    ...I'm realizing I may be taking this more strictly than others. :p There are even cases in the Unity sample projects where they follow this same pattern (though those also make heavy use of [Inject], so it's hard to know how closely they match the current direction). Are you just not concerned about it?

    2. How many Entity Managers do you find yourself typically using in a project?

    Thanks!
     
    Last edited: Jan 3, 2019
  11. Floofloof

    Floofloof

    Joined:
    Nov 21, 2016
    Posts:
    41
    1) So you wouldnt do something like CreateEntity(ECSArchetypeComponents.SomeListOfStaticComponents) inside a job function. Instead when you create the job you pass in the required data you need like so.

    Code (CSharp):
    1.     public struct CreateSomeEntityJob : IJob
    2.     {
    3.         public EntityArchetype MyJobArchetype;
    4.         [ReadOnly] public EntityCommandBuffer ecb;
    5.  
    6.         public void Execute()
    7.         {
    8.             for (int i = 0; i < 100; i++)
    9.             {
    10.                 ecb.CreateEntity(MyJobArchetype);
    11.             }
    12.         }
    13.     }
    So you dont want to use static data INSIDE of the jobs but rather pass copies of that data to the job when you construct it.

    2) There should be on EntityManager per World. So how ever many worlds you have is however many EM's you should have. If you are doing mainly main thread work using the EM is fine but for anything job related you should really use EntityComandBuffers. Or alternatively Tertle has some solutions that reduce the use of EM and ECB.

    Also just to renote, I usually override the OnCreateManager method in the component systems and I use it like such
    Code (CSharp):
    1.     private EntityArchetype SomeArchetype;
    2.  
    3.     protected override void OnCreateManager()
    4.     {
    5.         base.OnCreateManager();
    6.         SomeArchetype = EntityManager.CreateArchetype(ECSArcheTypes.SomeArchetypeComponentList);
    7.     }
    8.  
    9.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    10.     {
    11.         var jobHandle = new CreateSomeEntityJob
    12.         {
    13.             MyJobArchetype = SomeArchetype,
    14.             ecb= endFrameBarrier.CreateCommandBuffer()
    15.         }.Schedule(this, inputDeps);
    16.  
    17.         return base.OnUpdate(jobHandle);
    18.     }
    If another system wants that archetype they can simply use EntityManager.CreateArchetype(ECSArcheTypes.SomeArchetypeComponentList) and the EM will give back the same archetype
     
    Antypodish likes this.
  12. Floofloof

    Floofloof

    Joined:
    Nov 21, 2016
    Posts:
    41
    This is just a solution ive found that works for me seeing how my game im refactoring is pretty small. So having 20-30 componentType[] isnt that big of a deal for me. Maybe the factory route is better for your solution or maybe something else.
     
  13. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488


    Of course!

    And if a Job can't know ahead of time which type it might spawn, I suppose you could just pass in a lookup table in the form of a Native collection. (Please let me know if you don't like this pattern).
     
  14. Floofloof

    Floofloof

    Joined:
    Nov 21, 2016
    Posts:
    41
    I mean it really depends on what your use case is but that would work for this case. Not sure what the "standard" is for this type of stuff tho if thats specifically what you are looking for.
     
    orionburcham likes this.
  15. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    One question about this. Since these are managed arrays, you can't use them in Jobs. How are you using these ComponentType[]'s?
     
    Last edited: Jan 4, 2019