• Advertisement
Sign in to follow this  

Component-based architecture - entity and component storage

This topic is 1095 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

 

I recently decided to move my game from an inheritance based architecture to entity-component based one. I have the whole Component system set up, but I'm having problems figuring out how to store and pass them around. Below is a bad image of my architecture.

 

[attachment=26133:Game Architecture.png]

 

Right now the operations are as follow:

The Game class passes information and objects between classes as well as contains the mainloop

The LevelLoader class reads the tile info from a .tmx(TIled) file and generates the tile Entities, storing them in one of three vectors based on type: foreground, background and collision box. I separated them to make rendering and collision simpler. After loading is complete ALL the Entities are transferred to the GameObjectManager class.

Every frame after this is processed as follows, the GameObjectManager updates all its entities, which update their components, then Game extracts the entities vector from GameObjectManager and hands either the vector or elements of the vector individually to the different systems.

The Systems handle their operations and this cycle repeats.

 

This is where I feel like I'm making a mistake. I have the entities and I have the component, I just don't know how to give the different systems (Physics,Renderer Input etc.) access to them. Currently I just get the std::vector<Entity> container from the GameObjectManager class and pass it into each system, but it just doesn't feel right. Objects that have no physics component have to be iterated over in the Physics system and ones that are just empty collision boxes get passed to the Renderer.

 

One idea that came to mind was having a vector of pointers for each type of component and passing the corresponding vector to the corresponding system. This would certainly help the game run smoother, but I think adding new component types would become more difficult.

 

Another is to sift through the vector of entities in the Game class and just pass the entities components to the right system:

for (int i = 0; i < entity.size(); ++i)
{
	if (entity[i].hasComponent("phsyics"))
	{
		physicsSystem.Update(entity.getPhysicsComponent());
	}
	if (entity[i].hasComponent("render"))
	{
		renderSystem.Update(entity.getRenderComponent());
	}
....

}

With all this said, my question is, what is the most efficient way of handling entity/component storage and accessing. Should I throw them all in a single container and just pass it around and let the systems sift through? Or should I manually sort the components beforehand and pass the systems only what they need? Is there a better/smarter way of handling this?

Share this post


Link to post
Share on other sites
Advertisement

As you may be aware, there are a large number of different implementations of component based systems.

Especially in languages such as C++, the benefits of ECS designs can be the ability to employ contiguous memory. If you have each system allocate a pool of components, then you can reduce the frequency of cache misses, and hence improve performance of system updates.

 

Hence, when you register a component with an entity, it makes sense to request it from a system, which will maintain an internal pool (and therefore update internally) of components.

IComponent* physics_component = physics_system->create_component();
entity->components.push_back(physics_component);

// Some time later
double delta_time = 1.0/60;
physics_system->update(delta_time);

This has its difficulties when you have systems which share components with other systems. Generally speaking, avoiding this dependency helps a great deal.

Share this post


Link to post
Share on other sites


[...]The LevelLoader class reads the tile info from a .tmx(TIled) file and generates the tile Entities, storing them in one of three vectors based on type: foreground, background and collision box.[...]

Thing to remember is that ECS is for game objects. I'm not sure whether you consider here to wrap everything into an entity. That is not necessarily a god idea.

 


Another is to sift through the vector of entities in the Game class and just pass the entities components to the right system:
Code here

That approach is definitely not what you want, because that code snippet processes each entity individually from input down to rendering in one go. That is almost ever wrong. Instead you want to want let all interested entities be input processed, then let all interested entities ..., then let all interested entities be rendered. (In the meanwhile Krohm has posted an image that shows what I mean ;))

Share this post


Link to post
Share on other sites

Just because you can put arbitrary "component" object handles inside an array, which allows you to build "entities" does not mean you are component based.


Then you have a really weird definition of a component. His game objects are very clearly composed and not monolithic. That's all using components means, in any context; ECS is _hardly_ the first place the word "component" has ever been used in computer science or even game development. smile.png

The OP's architecture isn't particularly efficient, no; I'd go so far as to say it's actually the least efficient component system I've seen yet for a game engine, though it's not unlike some of the component systems you might find in certain Web or UI frameworks. Simply looping over every component and calling a virtual Update method on it would likely be more efficient... and is good enough to ship a commercial AAA game with.

Share this post


Link to post
Share on other sites

As you may be aware, there are a large number of different implementations of component based systems.

Especially in languages such as C++, the benefits of ECS designs can be the ability to employ contiguous memory. If you have each system allocate a pool of components, then you can reduce the frequency of cache misses, and hence improve performance of system updates.

 

Hence, when you register a component with an entity, it makes sense to request it from a system, which will maintain an internal pool (and therefore update internally) of components.

IComponent* physics_component = physics_system->create_component();
entity->components.push_back(physics_component);

// Some time later
double delta_time = 1.0/60;
physics_system->update(delta_time);

This has its difficulties when you have systems which share components with other systems. Generally speaking, avoiding this dependency helps a great deal.

 

 

This makes all of the sense. Thank you. I had been following the "Game Coding Complete 4" book, but I couldn't find where the actual components are created, so I thought "just toss em on the heap".
 

Edited by inzombiak

Share this post


Link to post
Share on other sites

My current issue was(is) with passing the information within the components. I'm not sure how this separation of systems would help.

 

I'm currently trying out Krohm's/Angus's solution. Its not as clean as having everything come out of a factory, but I can see how it would be better of the performance side.

Edited by inzombiak

Share this post


Link to post
Share on other sites

As a side note, if physics is your stuff, play with some physics API first!

 

 

Then you have a really weird definition of a component. His game objects are very clearly composed and not monolithic. That's all using components means, in any context; ECS is _hardly_ the first place the word "component" has ever been used in computer science or even game development.

Well, you got me there. I should have been more explicit in intending the word component in this case is to be intended uniquely as intended in CES.

 

Share this post


Link to post
Share on other sites

It was just a general system I added for the sake of explaining my situation, but will do.

Edited by inzombiak

Share this post


Link to post
Share on other sites

My current issue was(is) with passing the information within the components. I'm not sure how this separation of systems would help.

 

Physics and rendering are low-level systems or wrappers. 

 

The components systems settle at the highest-level module of the engine, which you can consider being the game module or the engine module. I would call the engine module since it brings all lower level system together.

 

Whenever you hear the word game entity you should think about working at the engine module level. The same thing obviously applies to an entity-component-systems.

 

What I'm trying to let you understand is that component systems are not modules by nature. You can implement with no problem component systems do limiting them to be high-level systems that uses lower level ones such that there are no dependencies. This helps when creating the overall high-level architecture of the game engine level by just connecting the dots.

 

 

 


As a side note, if physics is your stuff, play with some physics API first!

 

Agreed. I got a vote up indirectly.

 

If you use Bullet Physics or Box2D then the physics system works like some kind of high-level interactivity of the user and the physics world; the physics world it is already implemented in both libraries and you should avoid duplicating functionalities.

 

If your renderer works like a scene manager or a game world I have no words here, but if is a low-level graphics wrapper then things will be quite complicated in the future when you do more advanced stuff like cross-platform support, because the system depends of the game-object components.

 

The game object manager looks like a scene facade class and probably most of the time containing game objects/actors/entities/whatever is in this category; this is more correct than calling a class "the render system", creating components such "renderables", etc. which would be quite complicated to generalize this since its small functionalities differentiates a lots sometimes.

 

That said, thats what I would do if I had your architecture:

 

Separate systems into low-level ones and high-level ones such that you know what a module, a system, and a sub-system is;

Review your classes responsabilities and functionalities;

See if you can port any other external library such a physics library with no problem to the component systems;

 

Interesting how people tend to implement their ECS such that every system replace a functionality that could be easy and correctly implemented separating into low-level modules and high-level modules. IMHO separating responsabilities is a tool that can be used to switch from ECS and the old game-object model such entities or the simplest OOP design.

 

The main idea of the ECS or the game-object component system is that you should avoid inheritance when possible by just using the "plug-in the component" approach. It doesn't mean that you must aways create a component to every type of system, it is just an way of organizing the components such that the entitiy is just an ID, of course with a lot of optimizations in order to be fast, compatible with script-systems and event based games (tough games are not event based), etc. It also solves some problems with weird class inheritances; but since that's run-time you can most of the time skip that and duplicate classes functionalities.

Edited by Irlan

Share this post


Link to post
Share on other sites

Irlan: So if I understand what your're saying correctly, it is that by separating my systems, I can create an architecture where I can swap out low-level systems (e.g. swapping Bullet Physics for Box2D) without having to change the high-level systems.

 

Krohm/Angus: I have each system creating and managing its corresponding component types, but in order to avoid a major system overhaul, I made the createComponenet() functions and the containers that hold these components static so they could be accessed from my component factory as such

m_componentFactory.Register<TransformComponent>(TransformComponent::COMPONENT_NAME, TransformManager::CreateTransformComponent);
m_componentFactory.Register<RenderComponent>(RenderComponent::COMPONENT_NAME, EntityRenderer::CreateRenderComponent);
m_componentFactory.Register<InputComponent>(CInputComponent::COMPONENT_NAME, InputManager::CreateInputComponent);

*To create a component I call*
m_componentFactory.Create(GetIDFromName(currComponentNode->Attribute("name")));

I know statics aren't the most popular kind of variable, but would this be considered a horrendous thing to do? It just makes my code a lot cleaner and a lot organized.
Sorry for the branching questions and thanks for sticking around.

Share this post


Link to post
Share on other sites

Just reading your first post and from the diagram you provided in it, to be quite honest that structure wouldn't be too bad for using alongside ECS. Notice that I said alongside, and not as a direct part of ECS, because physics and rendering can be very involved and require complex logic that shouldn't exist in the context of entities and components anyhow. 

 

Other logic, like level loading can create entities for you, but doesn't necessarily be a system or part of one, especially if its purpose is only to load a level. It is not going to run on every frame, and that is how I have my level code setup. When I start the game, a LevelLoader class is created on initialization, after the ECS framework is initialized. It takes a EntityBuilder instance for a parameter. The LevelLoader instance resides in the scope of the game but outside the ECS.

 

The LevelLoader's logic is, using EntityBuilder, simply to add all the Entities necessary that make up a level at the start of a game. Add tiles, add players and enemies as well, and that's it.

 

After this point, EntityManager has all the Entities needed for the System processes to use in the game loop.

Share this post


Link to post
Share on other sites

So if I understand what your're saying correctly, it is that by separating my systems, I can create an architecture where I can swap out low-level systems (e.g. swapping Bullet Physics for Box2D) without having to change the high-level systems.

 

In this specific case it is impossible to swap because both libraries do not work with same degrees of freedom.

 

What I am saying is that you should be able to plug-in one of those libraries with no problem; that's a good signal that you're not mixing responsabilities.

 

You could be able to swap between any game-object model (ECS, OOP, etc.) if you work with modules. The file module, the graphics module, the memory module, model, terrain, etc. Then your systems are nothing more that a high-level wrapper class that the game can know.

 

I'm seeing a InputManager in the code you posted; that's why I don't like the ECS without stablishing some rules before; you can get into serious dependencies problems. The input manager will abstract everything related to input? 

 

I think that there is no need of a TransformManager. Basically every interactive entity in your game will have a transform component, and doesn't make sense to just manage transformations. They're not resources and have no kind of specific responsability unless holding a transformation matrix and have functions to change its orientation representation.

 

I've repeated that 10^100 times, but I would let like this (pseudo-code) if I would be implementing a fast and robust component system:

 

CEntity (an pointer or ID);

CGameObject (has a transform object and a array of pre-defined and enumerated components and a pointer to a entity);

CComponent;

 

CPhysicsComponent (has a array of rigid bodies);

CRenderableModelComponent (has a pointer to a renderable model);

CLightComponent

CDirLight...

CSpotLight...

CConeLight...

 

CPhysicsSystem (owns the physics components and perform operations such do-ray-cast and return a entity so I can get a specific component of that entity be look-up-in-the-components-table, etc. you got the idea).

 

CScene (CSceneSystem?) has lights, renderable models, etc... //weird variable name, just for claricity.

 

The scene can have a instance or pointer to a CPhysicsSystem because it is what interacts most with. You can also create some kind of top class to work like an intermediate bridge between them.

 

I won't say any details because I gave up implementing a ECS when having no scripting systems.

 

It is important to know that component table look up should be fast. I believe that there are much better ways to do this with hash-functions but if I would simplify by taking the easiest approach, which is that pre-defining component types and performing a fast look-up.

Edited by Irlan

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement