How Does An Entity System Work?

In a previous article I attempted to make a case for using an Entity System as an architecture for game engineering. In this second article, I explain the core mechanism that makes an Entity System tick, and broadly how it is implemented.

Games in which the state of objects are in constant flux – arcade games – are very different beasts to other kinds of software application. Most of the time, an application has a stable state, and responds to external input by updating the state, and then surfacing that change to users. Arcade games by contrast demand change at regular intervals. The simple expression of this is that at the heart of every arcade game is a ‘main game loop’, which is fundamentally a list of state-changes that happen with or without external input, culminating in the surfacing of those changes to users by re-rendering the screen.

An Entity System manages that loop for you by offering a simple interface for all the algorithms that update the game: the System, and a mechanism for pulling into those algorithms all the data that needs to be updated, the Collection.

Entities, Components and Systems

An Entity System is an architecture for developing games that maintains a strong separation between the data that describe the game’s state, and the algorithms that express the game’s behaviour.

Entities are the atoms of an Entity System. An entity is a stateful, independent thing that persists over a period of time during a game. In an Entity System, an entity begins life as a stateless container and is populated with components, which ascribe state to the entity.

Systems contain the algorithms that read, manipulate and write the entities’ states. Commonly, systems iterate once per-frame, and iterate over a collection of entities (although several edge-case scenarios exist which complicate this simple structure).

Defining Collections

Collections are subsets of entities defined in terms of their components. A collection is defined as an array of necessary components: an entity that contains all the necessary components is a member of the collection; an entity that does not have one necessary component is not a member.

The collection is the primary mechanism that bonds together the entities, components, and systems. Most systems depend on and iterate over one collection per frame.

Imagine a simple game in which some entities with a Position are in motion defined by a Velocity. This imaginary MovementSystem will iterate over all and only those entities that have the two components Position and Velocity.

In Dust the MovementSystem would be defined like this:

systems
  .map(MovementSystem)
  .toCollection([Position, Velocity]);

Systems are executed in the order that they are mapped, though they may also be weighted, so that higher-weighted systems precede lower-weighted systems. In general, the iterate method of each system is called each frame.

The system itself may then resemble something like this:

class MovementSystem
{
    @inject public var collection:Collection;

    public function iterate(deltaTime:Float)
    {
        for (entity in collection)
        {
            var position:Position = entity.get(Position);
            var velocity:Velocity = entity.get(Velocity);
            position.x += velocity.dx * deltaTime;
            position.y += velocity.dy * deltaTime;
        }
    }
}

The system relies on the collection having only those entities that have both Position and Velocity components. If such an entity has the Velocity component removed, then the next time the main game loop runs, it will not be a member of the MovementSystem‘s collection.

Maintaining Collections

Importantly, there are usually many systems that use any given component. A Position may be written in a MovementSystem but will be read in lots of other systems. The diagram below shows the structure of an imaginary arcade game. Each color represents a collection for the corresponding system, defined by the corresponding components.

systems

My preferred way to maintain which entity is a member of which collection is to assign to each component an integer, and then maintain for each Entity and each Collection a Bitfield that describes their structure.

Consider the following diagram, which reflects the structure of an entity with various components added. As components are added, the corresponding bits are set, and as components are removed they are removed, maintaining a bitfield that can be described as an integer (for applications with more components than the biggest int has bits (commonly 32), a more complicated bitfield has to be defined as an array of integers):

entity

Collections are similarly defined as a bitfield corresponding to their necessary components. In this way, whether an entity is a member of collection or not is reduced to a simple bitwise calculation (~entityBits & collectionBits) == 0:

collection_satisfaction

Membership of a collection is not checked every iteration. Generally, entities’ structure remain fairly stable so when a component is added or removed the change is cached, and then at the start of the iteration loop collection membership is updated. In Dust, this process is itself defined in a system, the UpdateCollectionsSystem.

Criticisms

A common criticism of this approach is that it is wasteful. If an entity’s velocity is zero, why update the position; if it doesn’t move, why redraw it? At first look, it appears that this approach to architecture will lead to an extremely slow, expensive application. The response to this is in two parts: we approach these problems scientifically (because we can), and when the criticism is true, we fix it.

The structure of an Entity System lends itself to measurement. If all algorithms in your main-game loop are wrapped into a unified interface and called from a centralized place, then it is relatively simple to measure how much time each of them takes. Each system can be wrapped in a mechanism that records the amount of time a system takes to run. As needed, rolling or total means and distributions can be calculated and surfaced and analyzed.

The simplicity of measurement leads to a the approach of writing systems in the simplest possible way to achieve functionality, and then moving on. If later on, performance becomes a concern your metrics will identify the main problems for you. Premature optimization is a waste and is easily worked around.

When a problem is identified, there are many options for remedying it. If an AI loop that makes decisions based on surrounding objects is slowing down the application, it may be enough to adjust how often it runs from once-per-frame to once-per-second, or slower. If the renderer is causing headaches, then the solution becomes more complex. Moving from a full-blitter to a dirty-blitter on a CPU, or more ensuring batched draw calls to the GPU are difficult problems. In these sorts of cases, I have found the problematic system can be refactored to produce manipulate a data structure that is then passed through to a separate, architecturally-agnostic module specific to the high-cost task.

Of course, if for a particular arcade game many or most systems are merely placeholders or input aggregators for other modules, then there is a strong case that an Entity System architecture is not appropriate for that game. To that application’s developer: Good luck! It must be really something; please let me know what architecture does work for you!

Conclusions

The Entity System architecture is an opinionated architecture; it wants you to code in a particular way. Instead of creating classes for your game entities and encapsulating functionality, it expects you to treat all entities in the game as fundamentally the same kind of thing: the Entity, to which stateful Components are added.

The architecture’s utility then comes from how it allows the definition and management of Collections that ensure systems only iterate over entities appropriate to them. A simple, efficient mechanism for this is to express the configuration of an Entity and the definition of a Collection through bitfields, which can then be manipulated quickly to establish collection membership.

In an up-coming article I will demonstrate how to build a simple arcade game with my current Haxe Entity System library, Dust. Dust will remain as an early alpha until I release my first dust-based game (in production), after which I will tighten up the API and release it on haxelib.

6 thoughts on “How Does An Entity System Work?

  1. >The Entity System architecture is an opinionated architecture; it wants you to code in a particular way

    So far opinions have flown about and granted the experimental implementations available in the wild do force code toward game specific use cases. Some of your examples above are in that general direction. IE the bit field direction is a downstream concern for particular use cases, but on a quick look at your code it’s front and center.

    At the heart of things the entity system architecture is a component oriented architecture. I advocate for the component architecture to actually be a superset to an entity system or more defined constructs specific to game development. I’ve had good success applying the larger component architecture concepts well beyond just game development.

    Best of luck!

    1. Michael, Please forgive me when I say that I am unable to follow pretty much any of this comment. It sounds like you know what you’re talking about, and if so I would love to understand the nature of your feedback. Unfortunately it is all a bit too highfalutin for me to follow.

      Do you think that my my opinions have flown, or in general, do opinions fly about in the broader community? In either case, what are the opinions? Are they just opinions because I don’t support them with argument or evidence? Which ones?

      How should a bitfield be a downstream concern, and in what way is it front-and-center in my code? If my code puts it front-and-center but it should be downstream, is that also a criticism? What should I be doing differently?

      What architecture that is a superset of an entity system do you advocate? In what way does it include the entity system as a subset? What are the more defined constructs specific to game development?

      Thanks in advance.

      1. I should start with mentioning that anyone exploring and writing about ES based architectures is a good thing! I have been working in this direction for 4 years now with a significantly large code base that originally started years earlier under traditional OOP architecture directions. Having gone through the horror of trying to create a generic entity system by traditional OOP means led me to the component architecture / implicit composition approach. At first I applied this new approach to just the ES / game design aspect, but then generalized it to apply it to the entire architecture of the code base. For GUI systems, for runtime control flow beyond game design purposes, among other purposes, etc.

        >The Entity System architecture is an opinionated architecture; it wants you to code in a particular way
        >What architecture that is a superset of an entity system do you advocate?

        I put forward that there is a way to generalize larger architecture concerns such that there is more flexibility in approach. At the highest level the goal is modeling code around implicit composition with a focus of separating data from logic which essentially is high level data oriented design (DOD). Your implementation makes certain assumptions that limit this flexibility which work for basic game design purposes. There are also strict limitations imposed on processing that defeat or lose out on potential DOD principles. IE I don’t have a problem with the remaining paragraph from where the quote above is taken, but the following paragraph:

        >The architecture’s utility then comes from how it allows the definition and management ofCollections that ensure systems only iterate over entities appropriate to them.

        Therein lies the rub… This is a downstream concern and by making it the only way to structure control flow in your architecture is what forces coding / design into a particular “opinionated” way. Your current implementation is not the only ES that forces similar control flow. Artemis with its “Aspect” implementation essentially is very similar in this regard. I suppose what rubs me slightly the wrong way is that this is one approach of many to ES architectures. It is a type of implementation versus _the_ defining characteristic of all ES architectures.

        Before I dig in when I refer to components below I don’t mean a component as in just data versus system / logic. This viewpoint somewhat originates from Adam Martin’s blog posts. My viewpoint is that a components can refer to data, system, and additional component types. A container / component manager / entity can be considered a third type of component which is a “manager” type; neither data or system. This opens the door to compound entities by treating the container as component. Once you do that though the type of mapping functionality found in your architecture essentially breaks down.

        Some of the limitations are:

        - a 1:1 mapping of components. It is useful to be able to store a collection of components of the same type in a component manager / entity. Presently with your architecture and many other implementations in the wild type proliferation is necessary to store multiple instances of similar components. That or a separate data component is required which stores a collection of components, but this breaks implicit downstream access. This 1:1 mapping works for basic game design. This also prevents a very powerful pattern based on implicit composition that mirrors OOP / inheritance which is “extension by iteration”. Instead of specialization through inheritance hierarchies a component manager / entity, etc. can provide similar and even dynamic specialization by iterating over a collection of systems each of which provides further processing in a manner similar to inheritance and overriding parent class methods.

        - the inability to have nested components and managers (IE entities); no ability to create compound entities without hacks such as creating a holder data component.

        - your current architecture enforces sequential processing of entities requiring a decent amount of overhead in a system due to having to implicitly retrieve from each entity the components utilized in a given system. There is no way to do efficient broad phase iteration directly over just the data components required by a system.

        - Implicit composition easily exposes downstream mapping, but it is also very useful for child components to be able to reference parent containers and have upstream access. In this manner systems can implicitly find the data they need to operate on rather than the way your architecture works through the mapping of component managers / entities containing combinations of components to collections which are then associated with each system.

        - The type ID system of storing components in a bit field only works well in a single threaded app. It becomes messy in a multithreaded implementation requiring explicit synchronization let alone IPC and client server implementations where constant conversion between types to ID is necessary.

        - The System interface explicitly connects iteration with time based control. I do see that you are applying your ES approach to in app / game GUI. I find it interesting that your System interface hard associates time based control flow.
        ———–

        I do find your work with Dust interesting since I’m not overly familiar with HaXe. I find it curious that there are notes in GitHub that mention some of the code doesn’t compile to C++. I do like that you are making an attempt to use your ES beyond just game entity design and apply it to other runtime areas of your effort. As I mentioned it’s early days as far as generic ES architectures go. I have no doubt that your approach is working for you presently, but it may not be the most efficient nor is it entirely generic. There is a one shot direction regarding utility of your current architecture.

        There are critics of the ES approach and presently most of the implementations in the wild follow a similar trajectory to your architecture. It will be interesting to see how things develop in the coming years. I can’t wait to get my efforts out there that is for sure. Maybe this post adds fuel to the fire as far as further discussion goes.

  2. Nice articles. The noun-verb argument is cool, not seen that before. The bitfield stuff is a nice note.
    What I have a hard time with is what to do with behaviors that involve 2 different component sets. My game (most games) have plenty of these. For example in my game, the only collisions are between bullets and players. In OOP I would iterate over bullets and then players. Bullets apply damage to players and then are deleted. How would you map this to ES? To me, 2 component sets are involved (bullet/position/model, health/position/model). Should the system iterate over 2 sets? Nobody writing about ES discusses that. There are workarounds involving messaging, dynamic entity composition, etc, but this type of activity (one component set acting on another) is so common that it seems to me that a system should be able to do it more directly, i.e. internally. After all the core value of ES is about the independence between components and entities, the idea that you don’t care about what the entities are, only the behaviors. Hope you understand what I’m saying, would like to hear your thoughts. Hope you write more on ES. You write very well. Cheers

    1. Thanks Tom.

      I agree, Entity-System advocates don’t talk about the messy cases like the one you describe.

      In Dust I have the ability to map multiple collections: systems.map(BulletCollisionSystem).toCollection([Bullet, Position], "bullets").toCollection([Player, Position], "players"); and then use named injections in the System @inject("bullets") public var bullets:Collection; This is enough to let you create a nested loop so you can iterate over every bullet for every player…

      This of course, is only half the answer. There’s a much more interesting Computer Science question about how to minimize complexity in this sort of scenario as the number of elements grow (how to do all these collisions in faster than On^2 time).

      Perhaps rather than just writing this response and leaving that as a tease, I should write up this implementation and discussion in a follow-up blog article. I’ve been meaning to write more, but haven’t started one. I can put this at the top of my list. Thanks for the feedback!

      1. Cool. Entities acting on different types of entities is as common as mud in games and this will often mean different component sets. I think “ES advocates” should stop talking about systems operating on a single component set and instead describe systems as acting on one or more component sets (each set consisting of 1 or more components). A definition for a system would help here. “Minimal piece of code that does something useful with one or more sets of components” or some such.
        Anyway with your answer I’ll happily assume for now that the “proper way” to do ES is to have systems act on *multiple* component sets.
        Aside: seems like we are missing some nomenclature here. Common language for a component set. Common language for the data fields that make up a component (“attributes”?).
        Another question I have is whether the ES gurus typically use messaging between systems, or try to do keep things within the ES framework. E.g. When I handle a collision I could send out a collision event to event subscribers, or I could create a collision object that other systems act on. Would be nice to see some discussion among the “big boys of ES” about the advantages of each (not just related to performance).
        The performance aspect you mention (numeric complexity) is not related to ES, and to me at this time is less interesting than the ES topic (which addresses software complexity). I would only mention it in passing.
        Cheers, and thanks again.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>