conquistadorthegame-blog
conquistadorthegame-blog
Conquistador
1 post
A game development blog for a space shoot-em up game.
Don't wanna be here? Send us removal request.
conquistadorthegame-blog · 8 years ago
Text
Implementing an Entity-Component-System engine
Tumblr media
The game, having been developed for the most part from scratch, uses a custom engine, which follows an Entity-Component-System (ECS) architecture pattern. The reasons for choosing this pattern is that it allows for faster expansion over Object Oriented architectures due to its modular nature. This further allows the engine to be reused with the only modification being the nature of the components and the code executed by each system, which is highly dependent on the use-case.
The main concept of an ECS architecture is that components contain data about an entity which they are linked to. The components may then be used by a system; for example, a graphics component may be linked to an entity, and a graphics system may then render the component to the screen. In many implementations, the entity is simply a number or a handle, and components are bags of data, containing no functions. The majority of the code for the game logic is executed in the systems.
There are several approaches to an ECS architecture. They shall be detailed below with further reading material and relevant links.
Entity-Object Orientated
This approach assumes that entities are some form of an objects, such as a class, containing components. Components may also be implemented as classes. Each entity holds a list containing all the components it has. All the entities may then be stored in another list, which a system can iterate over.
Note that in C++, holding a single list of different components can be difficult to achieve. It can be done by having all components share a base class, and the list holds pointers to the base class. When a component is retrieved from the list, it is then downcasted - through either static_cast or dynamic_cast. However, usage of these casts is generally seen as an indicator of bad design. Furthermore, dynamic_cast is slow compared to static_cast, but static_cast is unsafe compared to dynamic_cast.
Alternatively, a component could be represented as a class containing a union of structs. Each struct in the union would represent a different component type. The overall class would have an ID linking it to the type of component, and to access the respective component, the struct’s object could be used, as seen in the semi-pseudocode below.
Further reading & references:
http://vasir.net/blog/game-development/how-to-build-entity-component-system-in-javascript
https://www.gamedev.net/resources/_/technical/game-programming/understanding-component-entity-systems-r3013
Component-Object Orientated
The component object orientated approach allows components to contain their own update loop, which is independent of any systems. In the game loop, each component is iterated over and the update function is called. To allow components to communicate with each other (for example, a graphics component would have to communicate with the physics component), each component has knowledge of the entity that contains it, and can query the entity for other components that it holds. Alternatively, components can have references to each other - however this is a comparatively inflexible and more complex solution. The most complex to implement but most flexible to use would be a messaging system between components.
Further reading & references:
http://gameprogrammingpatterns.com/component.html
World Orientated
A data oriented approach is the most flexible design for an entity component system. In this design, components are structs or classes holding the data. Each component type has a list. The index of the component in the list refers to the entity that it belongs to. This allows systems to retrieve data quickly, by retrieving the components at a particular index from multiple lists.
One issue with this design is that some entities may not have a specific component, but an entity created afterwards would. As a result, you cannot iterate over the component lists. To solve this, a “mask” is created. The mask is a binary number representing what components an entity has. The masks are similarly stored in a list, with the index number representing the components the entity has. Comparing masks to the components that a system requires is fast, and so systems can iterate from 0 to MAX_ENTITY_COUNT, retrieve the mask for that entity, compare to see if the entity has the required components before retrieving the necessary components from their respective lists.
Another issue with this method is wasted memory - components still exist in the list despite not being owned by any entities. Reportedly however, the memory not being used is negligible in terms of performance and efficiency.
Further reading & references
https://www.gamedev.net/resources/_/technical/game-programming/implementing-component-entity-systems-r3382
0 notes