Implementing ECS in C++ can be done by creating an EntityComponentSystem manager class, which holds entities and archetypes. Entities represent individual objects, while archetypes represent a grouping of components. If you think about an ECS Structure of Arrays (SoA) as a 2D data structure of rows and columns, the archetype is the set of all rows in that SoA and the entity uniquely identifies a specific column. Each unique archetype maps to a different SoA structure.

There are other ways of organizing the SOAs. See skypjack/entt for a different architecture, and probably worth just using their open source well-tested implementation unless you really want to implement your own architecture.

The ECS manager is responsible for being the primary interface for the user. The user can create a new entity. Add or remove a component from an entity (which moves it to a new archetype). Access component data for a given entity. Serialize and deserialize data to/from disk. And most importantly, efficiently query for all entities which have one or more specific components.

That serialization has a few gotchas associated with it. It's important that there aren't any pointers stored in components that get serialized, because they will be invalid on a reload. All data needs to be stored on the struct, and any pointers need to be set to null during serialization and automatically re-built whenever they are null.

One example is that my Sprite class holds a path name to the associated image file with a pointer to a different class that holds the texture buffer on the GPU. Each time the renderer tries to render a sprite, if the pointer is null then it re-sets that pointer using a cache that is built during startup. Every sprite with the same path ends up sharing the pointer to the texture cache so that it only gets loaded once into the GPU.

My UI elements have a similar problem because they use a lot of an Update component which just holds an std::function that runs every frame to do some sort of one-off task. To handle those I have a list of components which are considered UI elements and I exclude those from serialization entirely. My deserialization function deletes all existing UI elements and calls the function to build them again from scratch after deserialization is complete.

Back to Home