Sign in to follow this  

ECS design

Recommended Posts

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

 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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. 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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!!  :-)

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 

Share this post


Link to post
Share on other sites

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!  :-)

Share this post


Link to post
Share on other sites

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. Edited by away2903

Share this post


Link to post
Share on other sites
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.

Edited by bogosaur5000

Share this post


Link to post
Share on other sites

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?

 

Yes I could see how this would become a problem. 

 

From what I can tell, it might be best to just register the entity with the systems that would care for it, then just loop though those.

 

I have also seen examples that use nodes that store the needed components. Is this a good implementation idea or are their hidden problems. I would guess that you would need register a node with every system that would take a bit of time (but then again you would have to do this for entities aswell)

Share this post


Link to post
Share on other sites

Just use one of the already implemented ones out there.

 

Arr good to find some examples cheers for the links :).

 

I have one more question and it comes in relation to rendering. With using Opengl, creating a good render component and system seems pretty complex (due to getting the camera component, lights and data that might need to be linked to the shader for different objects). Has anyone got a good suggestion for solving this problem. I just dont see an easy way of creating this system without alot of small hacks (which is wat my current implementation has)

 

Again cheers for the help

Share this post


Link to post
Share on other sites
I have one more question and it comes in relation to rendering. With using Opengl, creating a good render component and system seems pretty complex (due to getting the camera component, lights and data that might need to be linked to the shader for different objects). Has anyone got a good suggestion for solving this problem. I just dont see an easy way of creating this system without alot of small hacks (which is wat my current implementation has)

 

This is just API stuff. If you don't know how to wrap it, the best tutorial I know is www.learnopengl.com. I don't say the code is perfectly optimized, but it's great for starters, you can optimize it on your own. And maybe you can get some ideas from there. I'm sorry that I can't help you in a more specific way, but I've never encountered your problem.

Edited by bogosaur5000

Share this post


Link to post
Share on other sites

Well.. one of the easiest ways to make sure that you don't have too many levels of communication between components is to make them higher level. I see a lot of times people separating components into extremely small functionalities.. So there may be a TransformComponent, then a PhysicsComponent, then a RenderComponent, VelocityComponent, HealthComponent, DamageComponent, and it keeps going on and on. 

Share this post


Link to post
Share on other sites

This is just API stuff.

 

Sorry, I didn't mean that I not know how to get everything drawn to screen (i have classes that deal with thinks like binding VAO's and using the shader program so all the linking is easy enough to do)

 

Ill try and give an example. So for my current rendering system I would have to have the following

 

  • A entity list for things that i want rendered
  • a entity list of things that contain a light component 
  • the camera

This is fine for the most part and very much do able. For a standard model i would only have to like the model view and projection matrix, i would also have to link the texture if the model has one (simple bool check) and link the lights to the model (again easy enough). The trouble im having comes when a shader need more then this. It would seems that say i needed to pass another bool to a shader for some reason, i would have to define this with another component then bring it into the rendering system and check it exists then pass that components data to the shader. This would quickly get very complicated and messy for the system. The only other solution i can think of is the have a render class for each different type of object and have all the linked passed to it but this would still involve passing data from components. 

 

Hope that gives a better explanation of my problem 

Share this post


Link to post
Share on other sites

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.

 

In one ECS design that I really like from research, but haven't had a chance to personally explore in code, Components are *only* data, Systems operate on the data, and Entities are just an ID.

 

Each System make use of containers of components, and map the EntityID to components when needed.

 

When starting off, here's a few things that people widely do that I personally think are bad design:

 - Don't make Components inherit from a base component (even if it's CRTP).

 - Ditto for Systems. Don't make Systems inherit from a base system (even CRTP).

 

My rational for this is stated in another thread - others disagree. I'd start off NOT making them inherit at all, because I think it messes up the entire way your interfaces will be designed; but if you later find you *need* it, then later you can rewrite the interfaces easily enough (easier than going the other way).

 

Additionally:

 - Don't limit Systems to one component type. A System might want more than one type of component. Don't assume a one-to-one mapping of system and component types.

 - Many Systems might need access to the same components. Don't assume only one System can ever use the same container of components.

 - Some component containers are public (used by multiple systems), others are private (a single System uses them internally for bookkeeping).

 - Don't assume every type of component needs to be stored in the same way. Some types of component are better in arrays, others in maps.

 - If a component contains a single variable, something's likely wrong and you might be breaking your components up into too fine a granularity. However, if your game genuinely needs it (again, I think it's a code-smell), then you may want to make a Property system for your ECS.

 

 

For Components in arrays, this is done for speed in many cases - in those cases, you should double-indirect to map your EntityID to the component:

//Conceptually:
std::unordered_map<EntityID, IndexOfComponentInArray> componentIndexMap;
std::vector<Component> components;

myEntityID = 357;

auto iterator = componentIndexMap.find(myEntityID);

if(iterator == componentIndexMap.end())
{
    //...entity doesn't have this kind of component!
    //This indicates a problem in your run-time *data* (your scripts, or whatever), and should be hidden gracefully (and reported to the log).
}
else
{
    IndexOfComponentInArray index = *iterator;

    if(index >= components.size())
    {
         //Error! Out of bounds index. This indicates a flaw in your *code*.
         //If performance is genuinely required and proven (for example, if this is running on a server), this check can be disabled in release mode (but by default, shouldn't be).
    }
    
    Component &myComponent = components[index];

}

(I'd wrap this type of container in it's own class)

 

This only needs to be done when looking up a component using an EntityID, which is much rarer than just iterating over entities. (and again, this is only needed for some types of components where performance is measured to be necessary). Two benefits of this is that you save space for the many entities that *don't* use the components (you don't have empty unused elements in the container), and also that the System can re-arrange the components for optimization without invalidating the EntityIDs trying to access the components.

 

I'd also suggest one other thing: I personally believe making general-purpose engines are a waste of time :P, unless you are selling the engine itself commercially. I prefer engines that are designed for a specific genre of games and with a specific game in mind, that can be easily modified to accommodate additional games of the same genre, or heavily modified to accommodate games of different genres when actually needed. Over-genericness can waste more time than it saves. So while making your code, I strongly suggest you do so focusing on a specific game you are making, so you don't over-engineer yourself into not releasing any games and only having an engine that works for no games.

Share this post


Link to post
Share on other sites

might try this:

 

an entity is a vector of ints. each int is the index of a component in a vector of components, or -1 for "doesn't have one"

 

components are kept in separate vectors for each type of component. their indices are used in the "entities list" to indicate which entity "owns" them. use "add at end" and "swap and dec" to keep all vectors packed and cache friendly. all iterations are performed on component lists, not the entity list.

 

animation controllers are just another component. they have one or more  skinned meshes, animations, textures, and materials associated with them (all stored in shared resource pools and referenced by IDs). with later versions of DirectX etc, this also means they may have one or more shaders associated with them (just another resource).

 

so the entity in the "active entities list" points to the animation controller in the "active animation controller component list", which points to the resources.in the various shared resource pools (meshes, textures, animations, materials, shaders, etc).

 

when you add an entity, you add all its components to the various component lists, and set all its component IDs.   this includes things like passing the animation, mesh etc to the animation controller when you create it.

 

when you remove an entity, you remove all its components from the various component lists, including destroying or releasing or reducing the reference count on animation controllers, etc.

 

and the list of animation controllers IS the list of renderable entities.

 

you may also find it helpful to use data structures that hold just the info for a task, such as render.   many tasks require more than one component.  if i were using an ECS , it would take the ani controller and other component info (IE mesh ID, texture ID, animation ID, frame #, material ID, location, orientation, scale, cull. clamp. alpha test, alpha blend, texture scale, etc) and place it into a struct for passing to the render queue.

 

EDIT:

 

Note that swap and dec and add at end is a two edged sword.   it keeps your lists nice and contiguous, but you have to fixup IDs when you move stuff.  since you only move at most one of each type of component when you remove an entity, this isn't that bad.

 

the other option is an "is_active" boolean for each component. you add at first inactive, and set active = 0 to delete. but then you get inactive components in the list (and thus the cache) when you iterate. and you have to check for "is_active" and only process if its true.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

So, I didn't read all the replies properly because I am a Vasari adept, and time indeed is short. I am mostly sure that someone somewhere said that if you hit a performance bottleneck twice, it's likely that the game logic is the cause of slowness, not iterating over the components. Anyway, here are the conclusions that I've arrived while programming my components and systems, but before...

 

Over-engineering is hell! If you're programming alone on your free time, over-engineering can take as much time as fixing stuff for jumping on too many template too early. Stay with the simplest approach that solve your problem, refactor when needed. And be careful with premature optimization.

 

Okay, now to the stuff. My (FAKE) ECS recently went through the third refactor. One of the causes of the last refactoring is that I wanted to use the simplest component storing system possible (a vector of smart pointer to components), while keeping a door open to the lightning fast, vector of classes contiguous on memory, ordered by entity id, thus increasing the chances of cache hit while iterating upwards. To achieve this, I've decided on some compromises:

 

 - Components are stored on special containers, whose single responsibility is to allocate and deallocate components from whichever data structure they use.

 - Entities stores a type of unique handle to components, that will bridge the communication with the container. An unsafe, raw pointer can be obtained for performance when needed, but overall, the indirection impact has been immeasurable. If the entity releases this unique handle, the component is destroyed.

 

So, the entities are the unique owners of the components (even though they're stored on containers), and they can create weak references for other components. The point of the weak references is that I decided that I want my components to have some logic, instead of being just data. All sort of problems came with this decision, but I'm sure all sort of other types of problems would come if I followed a pure ECS approach. Pick your favorite.

 

Back to the containers, the components can be iterated by a ranged for loop, directly accessing the component through an iterator instead of a handle. It is up to the programmer to guarantee that the container won't be "changed" while anything is iterating over it. The systems will simply use this ranged for feature to iterate over the elements.

 

Now, about the containers with multiple types, I'm thinking in adding a new type of set container that references entities that have a set of specific components. The entity, when receiving a new component, would fire an event that'd be consumed by the sets containers, which would analyse the entity's components and decide if the entity would be referenced or not. When removing a component, the entity would fire an event as well, so that the set containers could unregister it. But I didn't need this yet, since even my rendering system is way too simple for now (just the bare minimum for a deferred rendering).

 

I don't have any statistical data or citations to back up my belief, but I really do believe that even the most optimized engines out there will hit a bottleneck with the sheer number of entities and components. Usually, they'd go for alternatives such as batching stuff, building some tree structure to organize data and reduce the amount of objects instances, do some algorithmic changes to the way things behave, or even making an entire group of special cases - such as rendering, which eventually you'll worry about overdraw, draw calls, order of rendering, order of applying effects and whole new worlds of challenges and tears. I'd advise to double check if your bottleneck can't be solved by means of architecture, before dealing to heavily optimizing the components system.

 

When the optimization is no longer algorithmic or structural, code tends to get really, really really harder to maintain, a a lot more bug-prone over time.

Edit: changed a few things for easier reading, and added the last paragraphs.

Edited by Kirlim

Share this post


Link to post
Share on other sites

I really do believe that even the most optimized engines out there will hit a bottleneck with the sheer number of entities and components.

 

any code will bog if pushed too hard on too low end hardware.

 

a data oriented ECS designed to maximize cache friendliness ought to theoretically be perhaps the architecture (if not one of the architectures) that would bog down last. 

 

data oriented ECS cache friendly optimization is what you do when you've already squeezed every clock cycle out of render and you're still not quite fast enough.  Its an optimization method of last resort.  oinly heard of one real world case ever needing it. that case is written up on gamasutra.  might have been Spyro 2, or maybe Sonic 2.  Spyro 2 i think maybe. use of containers with built in runtime checks would not typically be used in such a system due to the additional overhead. you going ECS out of desperation to become more cache friendly, the last thing you want to do is burn clock cycles on runtime checks.

 

there are actually may types of ECS's, with two primary possible benefits:

 

1. the ability to define new entity types from pre-defined component types without recompiling (IE data driven). most all ECS's have this. never heard of one that wasn't data driven.

 

2. cache friendly design (data oriented - as opposed to data driven).  note that an ECS can be both. but data driven ECS != data oriented ECS.   data oriented ECS is a specialized design above and beyond just data driven ECS.

 

its the data oriented ECS that would probably be the most performant of all architectures.  but given the fact that render is almost always the bottleneck compared to update, data oriented ECS is typically an unnecessary optimization.

Share this post


Link to post
Share on other sites

where is it written that components must be data?

 

what about an AI component?

 

Nowhere said that components *must* be data, actually. It's often given as advice (much like immutability) to ease up things such as parallelism, which can become quite a challenge on its own. I've looked on both approaches, and both seems valid with their respective pros and cons, and affect heavily the architecture and the way that things are used or executed.

Share this post


Link to post
Share on other sites

 

The point of the weak references is that I decided that I want my components to have some logic, instead of being just data.

 

where is it written that components must be data? 

 

what about an AI component?

 

 

I write that components should be data.  :wink:

 

If Components are data (generally), and Systems execute code, it's far easier to parallelize, as well as generally makes the code cleaner because it's clear what part of the architecture is responsible for what.

 

The key is to realize that logic can be parameterized and transformed by data, and that code itself can be data (for example, callbacks and scripts (which are just runtime callbacks)).

But by thinking of Systems as Logic and Components as Data (i.e. input and parameters to the logic), I think it helps you keep your architecture organized and lets you know what is responsible for what, helping you recognize what parts of your code are actually sharable (and optimizable). It's obvious that code transforms data, but don't forget the subtler reality that data can also transform code.

 

Obviously this depends on what style of ECS you are using, but I think it's a good guideline; "Hold to it until you absolutely need otherwise (and until you triple-check your need)" kind of thing.

Share this post


Link to post
Share on other sites

What is an Entity?  A list of components, but you do not deduce systems on components, you deduce components on systems!

?A entity should just not be more than a bunch of systems working togheter on some data

// spawn wall for a 2D tile-based RPG
void SpawnWall(int x, int y){
    Entity wall;

    wall.RegisterInSystem( renderSystem).Default( "wall.png");
    wall.RegisterInSystem( positionSystem).Default( x, y);
    wall.RegisterInSystem( tileSystem).Default( false); //not passable
}

This is similiar to what I'm doing. Basically I'm looping this way:

?For each Entity type (a different spawn function)

 For each System in Entity Type

  For each Entity

    UpdateSystem on that entity

 

we do not have fastest possible iteration because each entity have more components so we skip several bytes each time we want to access the next component.

we do not have fastest possible code cache because it is possible each system is called more than once in different entity types.

 

But we avoid most bottlenecks (no lookup cost of hashmaps, no huge BUS usage for iterating each time again  over Whole RAM memory) while keeping flexibility (and no need to filter entities into groups for fast search which is just an API complication), as a side note, if components for a particular entity type are independent, we can layout the entities like struct of arrays instead of array of structs to improve cache locality (in theory we could partially arrange the layout internally according to some euristic, but that add much more complexity for almost no benefit), but that would be an auotmatic check of easy implementation and without any additional effort from the user.

?Oh and to detect entity type, I just have a order over systems. It is pretty fast finding if another entity with same systems (!= entiy with same components) exist and we will allocate it there. It is nothing more than checking if a string is in a dictionary, if yes increase the count for that word. Using a dictionary lookup when spawning something, and just once and only for entities, is zillion times faster than doing dictionary lookup for all components every frame.

?Note that I do not used any particular complex cache or Group system. Just dead simple and yet effective code.

?Assign each system a letter of the alphabet A,B,C,D,E,F

?then a entity will have some systems

?AB

DE

ABD

 

Entity update order will be the same of the string sorted alphabetically so:

?AB
ABD

DE

But we are really doing no sorting, the entity is added in the right bucket just right after spawning.

Edited by DemonDar

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this