Entity Systems
Entity system is a software architecture pattern where you have individual components and composition of components is used instead of inheritance.
- Functional programming is all about creating behaviour using functions.
- Object-oriented design has classes which are used to create object instances. All behaviour in the classes and instances of those classes.
- Entity system design uses systems, which contain entities which are made up with components. All behaviour is in the systems and components have only data.
Each entity is a concrete item which themselves have no data or behaviour. You can think them as an unique label for an object or container i.e. globally unique identifier.
If you have 100 identical tanks, you have 100 entities, not 1.
Each component is a possible aspect of a system item. Component is a chunk of stuff that provides a particular aspect to an entity. All data is stored in components e.g. coordinates are in here.
Bicycle is made of metal, can be used by human and used for transportation.
Bicycle is an entity.
Coordinates, size, weight, material, fact that it is used by humans and
fact that it is used for transportation are components.
Each system runs continuously and performs global actions on every entity that possesses a component that relates to the system. A system essentially provides behaviour for components of a given aspect.
There is a visual component that tells how an entity looks.
There is a physical component that tells where the entity is.
There is a drawing system that renders an entity based on these two components.
This allows using relational database to efficiently save program state. Entity is a database key, database query can be used to get entity components. This makes data flow and the system-logic feel smoother. No need for objects -> RDBMS -> objects loop.
Database Example
Entity system stored in relation database.
<TABLE>: COLUMN, COLUMN
// Knowledge is component data.
<entity>: entity_id, debug_label
<component>: component_id, debug_label, description, knowledge_table_name
<knowledge>: knowledge_id, 1..M columns for each piece of data
<entity_component_knowledge>: entity_id, component_id, knowledge_id
// You can split <e_c_k> table to sub-tables by component_id as each
// system uses all components with the same component_id.
// Assemblages are like classes.
<assemblage>: assemblage_id, debug_label, description
<assemblage_component>: assemblage_id, component_id
// You can cache <component>, <entity_component_knowledge>,
// <assemblage> and <assemblage_component> as they do not change much.
Tanks Example
int createTank() {
int newId = createNewEntity();
// Attach components to entity using default values.
createComponentAndAddTo( TRACKED_COMPONENT, newId );
createComponentAndAddTo( RENDERABLE_COMPONENT, newId );
createComponentAndAddTo( PHYSICS_COMPONENT, newId );
createComponentAndAddTo( GUN_COMPONENT, newId );
// Attach component to entity using custom values.
float[] gunData = getKnowledgeForEntity( GUN_COMPONENT, newId );
gunData[ GUN_SIZE ] = 500;
gunData[ GUN_DAMAGE ] = 10000;
gunData[ GUN_FIRE_RATE ] = 0.001;
setKnowledgeForEntity( GUN_COMPONENT, newId, gunData );
return newId;
}
Bomberman Component Example
CellPosition
x (int)
y (int)
ScreenPosition
x (int)
y (int)
TimedEffect
timeRemaining (float)
effectType (enum: SPREAD, VANISH)
Spreadable
depth (int) – decreases by 1 each square it spreads. At 0, it runs out.
spreadPattern (enum:
FLOW_AROUND_OBSTACLES,
FLOW_CARTESIAN,
FLOW_IGNORE_OBSTACLES
) – the Behaviour will interpret these
Score
kills (int)
deaths (int)
Destroyable
hitPoints (int)
Collideable
height (int)
– if height > Spreadable.depth, we flow AROUND instead of OVER.
Also, we can give players a “height” they can move over. This also
lets us implement “jumping” in terms of increasing (for a single step)
the height you can move over (makes it easy for us to have “jumpable”
and “non-jumpable” squares).
Damager
inflictDamage (int)
BombLayer – (something that lays bombs, not a rendering “layer”)
spread (int)
depth (int)
damage (int)
Teleporter
cellsDeltaX (int) – +1 = right one square, -1 = left one square
cellsDeltaY (int) – +1 = down one square, -1 = up one square
PowerupPlayer
addsFeature (enum: FLAME_LENGTH, BOMB_AMOUNT)
amount (int) – +2 has double the effect, +3 has triple, etc