Entity Component System

For the game Spite:SoC we (project group) built a 3D-engine from the ground up. We knew that we wanted to try and use a Entity Component System (ECS), and I wanted to try to write one. So that's what I did! The code shown here is very bare-bones, and has been iterated to better suit our engine by myself and other team members. To be fair to everyone involved, I will only show the groundwork I made for the ECS!

The component part of the ECS consists of three main parts, all held together by a coordinator class. Components are held in a Component Container. The container uses a static array to avoid any memory reallocation at runtime. When arranging the component containers, I had two choices; either matching the index of components in their order within the array (all entities in every container at the same index belongs to the same actor), or using a map to map entities to their respective index within the container. I chose the latter option. This allows us to pack the memory better. The negative aspect was that I now needed a way to keep track of which indices were free, and which were taken. With a tight deadline closing in, I chose the easy way out and decided to keep all free index numbers in a vector. Hopefully this would not affect performance too much.

The Entity Manager keeps track of all active entities, and which components each entity has. This is done with a bitset. Each component has unique bits. Reading online, I found someone using the term "signature" for this bitset. I thought it was a suiting name, so I adopted it into our system. When creating an entity, the system identifies each component used and sets the bitset accordingly. The Entity Manager holds an array of bitsets, with each index corresponding to one entity.

All work is done in component systems. These systems do not relate 1:1 to components - a system can use one or many different components. For example, the AI system uses the rigidbody, transform, and AI controller components. To keep track of which components a system uses, the system uses the same bitset as the entity manager does.

A component manager is used to keep track of which component types are registered, and their respective signatures. When a component is registered, the manager creates a new array of that component type and inserts it into a map, mapping it to its type name.

All systems can be sorted by priority, and be updated in this order.

As described earlier, all heavy work is done within each system. Here is an example of the Update function in the Movement System in "Stilla Natt". Here is where the component array packing helps out with performance. Since components are arranged in the same way as the entites are, there is no jumping back and forth through the arrays.