Component-based architecture - entity and component storage

Started by
12 comments, last by Irlan Robson 9 years, 1 month ago

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.

Advertisement

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.

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.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor


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.

This topic is closed to new replies.

Advertisement