ECS design

Started by
22 comments, last by DemonDar 7 years, 4 months ago

Hey everyone,

I have been trying to create an entity component system for a while and thought that i had a good lay out but now i have ran into a big problem.

My first implementation was based on entities holding all of their components in a vector. systems would then loop though all entities see if they have the entities they care about and then act if they had them.

As im sure you would be able to tell, this will hurt as we will loop though every entity and check if it has a component (or many more) which will hurt performance.

My second version planned to use an unordered map for every component type where the key will be the id of the component. This was looking good for a while but i ran into a problem with performance again. For systems I can loop though a component type but if i want a different component type (say for rendering i need model and texture and transpose component). I would need to do a find check to make sure that a map has a component of that ID. sadly this causes a massive loss in performance and leaves me where i was before.

So I guess really I'm asking for some guidance. Does anyone have a suggestion in a good way to create my entity component system to avoid performance problem. should i continue to use unordered maps or should I use vectors.

Thanks for reading :).

Advertisement

I don't know ECS systems, but assuming components don't change very often, why not make a list of some form that only contains the components that must be rendered?

ie a sub-list of all components.

(not even sure if "components" is the right word here! But I hope you get my point.)

A map is good for finding an element quickly (it costs computing a hash). Vectors are extremely bad at finding things, but extremely good at numeric indexing, and at sequentially walking through all elements.

http://www.insomniacgames.com/a-dynamic-component-architecture-for-high-performance-gameplay/ Gives a pretty simple approach. Your game objects will allocate a component from a system. Systems can register for certain update phases , i.e "PrePhysics" ... etc. The game will iterate through all systems and perform updates. Components also have the ability to ignore this automatic updating, and be updated explicitly by their owning entity. This method allows you to keep cache coherency , by updating per system.

There is also the idea of a layer of indirection and the use of the observer pattern.

Consider for a moment when an entity or component is either added or removed. In these cases, systems would be notified of the state change, allowing them to deduce whether the entity should remain registered in the system or removed. It's at this point where the system would also create a single, unified representation of state that it iterates during the frame update.

One benefit here is that your system update loop no longer must be concerned with determining the right entity list to update. When entities or components are added or removed, that internal unified representation is manipulated, which is the basis for your game update loop for that system.

Now the system's update becomes focused solely about data. Perhaps some event sourcing system is used to track state change and replicate it to other systems. Perhaps the system uses incremental small update loops to replicate state from one component to the unified representation. Perhaps your unified representation holds a weak-reference of sorts to the real components, which allows for quick, easy lookup of the component. But the goal here is that the system should be as solely based on the unified representation as possible, not the component.

In short, I prefer to view components as simple data bags who's sole responsibility is to seed user state to the simulation and allow easy ways to query and get state from the entity system. The introduction of the extra layer of abstraction with a unified system keeps system-private state out of the component, allowing the component cache array to remain as small in size and on par with its intent. Should one system need something specific not exposed by a component from another, perhaps an event should be raised to send that value across systems or merely have system 2 consult system 1 for said data.

The extra layer of abstraction also allows the component and system unified object to change independently of one another. As new contextual values are determined for the system to operate, the internal representation changes without impact to the component. The only real consequence here is you need a few loops to copy data periodically, but such loops are again often cache friendly and benefit from parallelism.

I had a similar design issue a few months ago when I was trying to create a simple but efficient ECS code structure. I've seen ECS designs of varying complexity and tried to create some perfect general system but run into these kind of issues and got frustrated. So I decided to take a few steps back and tried what I thought was an oversimplified solution at first, but now I like it and am sticking with it because I'm getting results.

Basically, I try to ensure that any system only needs to access a single component type that is associated with that component, e.g. the rendering system only uses the rendering component, the collision system only uses the collision component, etc.. All of the benefits of cache efficiency go out of the window if you need to access another component in any system. The biggest culprit is usually the transform component. Many/most systems need to know the transform of an entity (or at least its position). So what I do is store a copy of the required data in each component so there is duplicate information. At the end of every step, if an entity has moved then I broadcast the new position over the event system and all systems that need that information store it in their local component. Obviously this will use more memory (to store duplicate info) but if cache efficiency is your main problem then it will help.

Just to flesh out some other details of my design (in case its relevant), I access entities via a handle/integer id rather than pointers (I've tried as much as possible to banish pointers from my code for reasons) and so need to use a simple hash table to access a component via its entity id. This does restrict one component type per entity, but this is probably not a bad thing. Also my component types are kind of hard-wired so there are a finite number of component types. And finally, I created my own simple container array classes rather than use stl vectors or maps since I don't always want to do everything on the heap!

This may not be the design of choice of many people here, but for my own purposes, it works!! :-)

Have your specific system to iterate the specific components instead of entities. For example, renderable components are rendered by a rendering system regardless of who the entities are, not rendering system searches for entities that have renderable components.

In general im hearing that i should register a component with a system and then have the system loop though those components, but what if a system requires knowledge of more then one components (like the render system).

Registering a entity to a system sounds like it could work (like as naros suggests)

So what I do is store a copy of the required data in each component so there is duplicate information

This sounds like a very interesting idea but i fear that this could become very complicated/memory heavy if a system would be interested in more then 2 components

This sounds like a very interesting idea but i fear that this could become very complicated/memory heavy if a system would be interested in more then 2 components

Complicated, not at all. If anything, much simpler since you're making each system much more contained with only the information inside its corresponding component. (Unless you're referring to the event/messaging which can get complicated but using events is a different topic altogether. This could be done in a simpler way I suppose by having the Render System directly access the Transform Components to make the local copies).

Memory intensive, possibly, if there were such a huge amount of information you would need to replicate. But this is always the trade-off with cache-efficiency. You can get better performance at the cost of memory. I've found however in practice that the extra memory overhead is not too great.

The other possibility I suppose, following on the other suggestion, is to say loop through (all of) the Transform Components first and make a local copy of all the relevant positions, transforms, etc.. in a local array. Next loop through the other components (e.g. Render Component) and then use your locally cached values. It's better than jumping back and forth between the Transform and Render Component arrays, but not as efficient cache-wise as my original suggestion. There are definitely many ways to do this! :-)

In general im hearing that i should register a component with a system and then have the system loop though those components, but what if a system requires knowledge of more then one components (like the render system).


Have a checker that the component won't work if it's not accompanied with required components. I'm sure component has a reference to an entity it is "attached" to,
so it won't be a problem to access or to check if dependent components are also with the entity.

I think Unity does that but I forgot which; when you add / remove a component,
it will tell you that it is depended by some components, etc (I think some even throw errors during runtime). This is inevitable.
Have a checker that the component won't work if it's not accompanied with required components. I'm sure component has a reference to an entity it is "attached" to, so it won't be a problem to access or to check if dependent components are also with the entity.

Yes, but this is not entirely an entity-system approach, because when one component has a reference to another, they are tightly coupled, and it is a step backwards, because you are using ECS to decouple stuff in order to have more flexible code, why coupling it again?

The other reason is that the update order becomes less obvious and you need to be more careful.

And also if you want to multithread, having independent systems is better because you can use a separate thread for a system.

And you need to create a reference for every possible interaction between components, and most of the time you don't need all the interactions and the pointers stay empty, which is a waste of space, but it's not a big deal.

The best way is a messaging system.

Although I don't like it, it's too much. What I end up doing is a little bit of everything.

This topic is closed to new replies.

Advertisement