Thesis - Entity Component System Architecture and Optimization for 10000’s of entities
LEGION: Thesis artifact
Legion is a game demo that I developed in order to explore creating a game with an Entity-Component-System (ECS) architecture. In the demo, the player’s ‘sword’ whirls around in a circle to the beat of the background track, slicing through enemies as it goes.
The game is build in my personal C++ engine, developed at SMU Guildhall, using my thesis ECS to automatically multithread systems in two ways: splitting system Run calls into multiple threads that each take a portion of the entities, and running multiple systems at a time based on their read/write privileges for each component array.
ECS is a constricting yet powerful architecture both on the code design front as well as the optimization front. Each system had to be designed for multithreading, meaning its read/write privileges had to be declared carefully, and the code within the system had to be tailored to work for both single and multithreaded.
One of the best, most useful features about ECS is the ability to turn systems on and off at runtime. The debug window above shows all of the systems that I wrote for Legion.
The above graph shows system concurrency in Legion. For a given vertical line drawn through the graph, those systems are running at the same time. As the number of systems grew, this graph became less useful, but it is still an important debug visualization tool for understanding which systems depend on previous systems to run before they can do their thing.
PROTOTYPE
Tier 1 of my ECS was a non-generic component system with Transform, Collision, Physics, Render, and Movement components, and their corresponding systems to iterate over tuples of components. I made the entities data driven so you could define an entity in Xml, stating which components it had to begin with, and components could be added at runtime to affect behavior.
Tier 1 ECS artifact
private: Entity m_entities[MAX_ENTITIES]; // SC: SingletonComponent SCWorld sc_world; SCRenderer sc_renderer; // C: component array CTransform m_transforms[MAX_ENTITIES]; CRender m_renderComponents[MAX_ENTITIES]; CPhysics m_physicsComponents[MAX_ENTITIES]; CCollision m_collisionComponents[MAX_ENTITIES]; CMovement m_movementComponents[MAX_ENTITIES]; // Systems in order or operation MovementSystem* m_movementSystem; PhysicsSystem* m_physicsSystem; CollisionSystem* m_collisionSystem; RenderSystem* m_renderSystem; };
I decided on fixed sized arrays for tier 1, because it was the easiest way to get the system working from scratch. Each entity knew which components it had based on which ComponentBit’s were set on that entity. For a system to know which entities to iterate over, all it had to do was a bitwise & with its component bits to see if it had all the necessary ones.
From there, I moved to a generic system using variadic templates in C++, allowing me to scale the ECS for up to 64 entity components. The speed of searching an entity for components using a bitwise & (where each component is a designated bit) was more important to me than the limitation of having a maximum of 64 components.