Entity: anything that exists in the game world, either visible or invisible e.g. player, tree, trigger box, patrol way-point. A sound is not an entity, but a sound source could be
Component: a discreet property of an entity that contributes to describing its presence in the game world e.g. position, velocity, colour. Components can be of varying granularity, a ‘camera’ component could contain various sub-components such as matrices and vectors describing the camera orientation, or a component could be a single element like ‘speed’
An entity can be thought of as the sum of its components
Systems: processes that operate on an entity’s components e.g. rendering system, collision system, sound system. They typically read in component data, perform a transform on it and write the result back
ECS: entity-component system. A conceptual model that defines relationships between entities, components and systems. Computer games are at their core about interactions between entities. These interactions can be irregular and complex. “This lever opens a door 50 metres away which lets out a cat that in 30 seconds will turn into a flying carpet the player can ride”. Developing a system that can properly handle such complexity is an open problem. An ECS is one of many approaches, it makes components first class citizens stored in a flat database like structure, and entities just an implicit and mutable collection of components. Game systems know nothing about what entities they are operating on, only their components.
Lamorna Engine’s ECS is loosely based on the one employed by the Unity game engine: https://www.youtube.com/watch?v=p65Yt20pw0g
In a classic ECS game entities are fully dissociated into components distributed across a large database. My approach takes a step back from this and attempts group entities with the same components together in order to be able to do indexed iteration on components. In line with the Unity approach these groups are called archetypes, and represent what would conventionally be an object type like a monster or weapon.
Components are simply structs with a small number of member components. The granularity of the components really depends on needs and circumstance, but as a general rule a components should have a small and directed number of members. Each component has an ID and it’s size is known for memory allocation.
Archetypes have a bit-mask identifying what components they contain, an entity count, and the component arrays. Within the archetype entities exist implicitly as an index into the component array. Each has a component bit-mask, allowing us to toggle components on and off at run-time using a simple bit operation. Entities can not be assigned additional components at run-time beyond what they are initialised with, but their existing components can be disabled and enabled. So for example the ‘collide’ component can be disabled to exclude an entity from collision checks temporarily.
Engine system functions are given a list of components to operate on. In a preliminary step the function parses the archetype list for archetypes containing the components it seeks, and returns pointers to the start of each component array for each valid archetype. These can then be iterated over with an index in the familiar manner. The component bit-mask used to match archetypes is also checked against each entity’s component mask. Thus archetypes containing system components are discovered, but individual entities with one or more of those components toggled off will be ignored.
There is some overhead in operating an ECS. In my version system functions are run cold each time, that is with no assumed knowledge of which archetypes contain which components, so the pre-process described above is run each time the function is called. However what we lose with this added complexity we gain by making functions wholly entity agnostic. Adding a new component to an archetype adds all it’s functionality without having to touch any system code. Decide that you want an entity to fly? Drop in the ‘fly’ component and providing you’ve done your work right the entity will fly.
AN ECS takes time to implement and balance but when set up it can be very powerful. It forces you to be rigorous in defining the boundaries of system functionality, and component scope. My approach has undergone a few iterations but is still in some flux, I’m find myself having to create components as simple identifiers as an aid to game systems, which seems to indicate that further refinement is needed.
ECS is in truth an overkill in a game engine as simple as Lamorna, however it was a good learning experience writing it.