How have you stored archetype references?

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!

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.

1 Like

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)?

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.

1 Like

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.

@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.

1 Like

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. :smile:

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.

3 Likes

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.

1 Like

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. :stuck_out_tongue: 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?

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

Thanks!

  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.
    public struct CreateSomeEntityJob : IJob
    {
        public EntityArchetype MyJobArchetype;
        [ReadOnly] public EntityCommandBuffer ecb;

        public void Execute()
        {
            for (int i = 0; i < 100; i++)
            {
                ecb.CreateEntity(MyJobArchetype);
            }
        }
    }

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.

  1. 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

    private EntityArchetype SomeArchetype;

    protected override void OnCreateManager()
    {
        base.OnCreateManager();
        SomeArchetype = EntityManager.CreateArchetype(ECSArcheTypes.SomeArchetypeComponentList);
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var jobHandle = new CreateSomeEntityJob
        {
            MyJobArchetype = SomeArchetype,
            ecb= endFrameBarrier.CreateCommandBuffer()
        }.Schedule(this, inputDeps);

        return base.OnUpdate(jobHandle);
    }

If another system wants that archetype they can simply use EntityManager.CreateArchetype(ECSArcheTypes.SomeArchetypeComponentList) and the EM will give back the same archetype

1 Like

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.

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).

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.

1 Like

One question about this. Since these are managed arrays, you canā€™t use them in Jobs. How are you using these ComponentType[ ]'s?