C++ – A more data oriented design approach to Entity Component System

Architecturecdataentityentity-component-system

I'm creating my first c++ game engine project (for learning purposes) and in it I've attempted to implement an entity/component system utilizing some data oriented design principles while also not fully giving up my object oriented way of thinking. I have data only component structs:

(Example)

Position.h

struct Position
{
    Vector3D position{ 0.0f, 0.0f, 0.0f };
};

which are stored in arrays within my SceneManager class:

SceneManager.h

class SceneManager
{
    //Some other code....

private:
    //So only systems have access to component arrays
    friend class BGraphics::RenderSystem;
    friend class BInput::InputSystem;
    friend class BPhysics::MovementSystem;
    friend class BPhysics::CollisionSystem;
    friend class BAudio::AudioSystem;

    //Component arrays. Each index of the arrays represents an entity.
    //So positionComponents.at(1) represents entity 1's position. 
    Array<Position, 10> positionComponents;
    Array<Velocity, 10> velocityComponents;
};

As you can see, my engine's system classes are the only ones who have access to the component arrays and so every system can process components. There are some other things involved in this setup that I don't want to get too deep into for simplicity's sake. My issues are as follows:

Each system accepts a SceneManager reference in an Update function like so:

RenderSystem::Update(SceneManager& scene)
{ 
   //Use scene component arrays necessary for processing...
}

What I'm struggling with is right now my components seem like global variables in that every system has access to them and can change their state. Though it seems like a lot of data oriented design involves structs like these with public data for which other functions can operate on, changing their state. With that being said, is there something I'm missing with my implementation that would make my component data more encapsulated and safer? How am I suppose to think about handling data in a safe way when utilizing data oriented design? Any clarification is appreciated.

Best Answer

Indeed, in your design, the SceneManager acts as a container for global data. This is the inconvenience of a data centric design that sees data as a flat passive structure, and which leave to subsystems' functions the responsibility to manage the data.

A first remark is that each of the subsystems has to know that your data is in an array of fixed size, and needs to know the number of active elements therein. This is a fatal dependency. If later you'd choose to use a dynamic vector, or a searchable unordered multimap (e.g. for targeting group of components corresponding to a game tag), you'd need to rewrite everything.

You therefore need a minimum of encapsulation for accessing to elements in the container (e.g. With an iterator like interface such as first(), next(), previous(), last() ). The containers would then remain private, and the access to the components would be controlled.

Nevertheless this kind of architecture will be difficult to maintain. If you encapsulate data and the interface for accessing it, you are in an object oriented paradigm. So why not go for it ? Your SceneManager would the maintain a a container of Components. The components would become more comprehensive entities, which know their own position and velocity, the other subsystems will then not know these internals, but use the object's interface in a well behaved fashion.

If you want to know more about such approach, I'd recommend you Mike McShaffry's "Game Coding Complete". This book gives full insight about a game architecture, including hints about when to use object oriented features such as inheritance, and when rather avoid it.