13

I'm designing a game engine for a top-down multiplayer 2D shooter game, which I want to be reasonably reuseable for other top-down shooter games. At the moment I'm thinking about how something like an entity system in it should be designed. First I thought about this:

I have a class called EntityManager. It should implement a method called Update and another one called Draw. The reason for me separating Logic and Rendering is because then I can omit the Draw method if running a standalone server.

EntityManager owns a list of objects of type BaseEntity. Each entity owns a list of components such as EntityModel (the drawable representation of an entity), EntityNetworkInterface, and EntityPhysicalBody.

EntityManager also owns a list of component managers like EntityRenderManager, EntityNetworkManager and EntityPhysicsManager. Each component manager keeps references to the entity components. There are various reasons for moving this code out of the entity's own class and do it collectively instead. For example, I'm using an external physics library, Box2D, for the game. In Box2D, you first add the bodies and shapes to a world (owned by the EntityPhysicsManager in this case) and add collision callbacks (which would be dispatched to the entity object itself in my system). Then you run a function which simulates everything in the system. I find it hard to find a better solution to do this than doing it in an external component manager like this.

Entity creation is done like this: EntityManager implements the method RegisterEntity(entityClass, factory) which registers how to create an entity of that class. It also implements the method CreateEntity(entityClass) which would return an object of type BaseEntity.

Now comes my problem: How would the reference to a component be registered to the component managers? I have no idea how I would reference the component managers from a factory/closure.

Vaillancourt
  • 16,325
  • 17
  • 55
  • 61
Gustav
  • 131
  • 1
  • 3
  • I don't know if perhaps this is meant to be a hybrid system, but it sounds like your "managers" are what I've generally heard termed as "systems;" i.e. the Entity is an abstract-ID; a Component is a pool of data; and what you term a "manager" is what's generally termed a "System." Am I interpreting the vocabulary correctly? – BRPocock Dec 22 '11 at 21:24
  • Take a look at http://gamadu.com/artemis/ and see if their methods answer your question. – Patrick Hughes Jun 09 '12 at 22:00
  • 1
    There is no one way of designing an entity system as there's little consensus over it's definition. What @BRPocock describes and also what Artemis uses hes been described more in depth on this blog: http://t-machine.org/index.php/category/entity-systems/ together with a wiki: http://entity-systems.wikidot.com/ – user8363 Aug 12 '12 at 09:50

3 Answers3

6

Systems should store a key value pair of Entity to Component in some sort of Map, Dictionary Object, or Associative Array (depending on language used). Furthermore, when you create your Entity Object, I wouldn't worry about storing it in a manager unless you need to be able to unregister it from any of the Systems. An Entity is a composite of components, but it shouldn't handle any of the component updates. That should be handled by the Systems. Instead treat your Entity as a key that is mapped to all components it contains in the systems, as well as a communication hub for those components to talk to each other.

The great part about Entity-Component-System models is that you can implement the ability to pass messages from one component to the rest of an entity's components quite easily. This allows a component to talk to another component without actually knowing who that component is or how to handle the component it's changing. Instead it passes a message and lets the component change itself (if it exists)

For instance, a Position System would not have much code in it, only keeping track of Entity Objects mapped to their Position Components. But when a position changes, they can send a message to the Entity involved, which in turn is handed off to all of that entity's components. A position changes for what ever reason? Position System sends Entity a message saying that the position changed, and somewhere, that entity's image rendering component gets that message and updates where it'll draw itself next.

Conversely, A Physics System needs to know what all of it's objects are doing; It must be able to see all world objects to test collisions. When a collision happens, it updates the Entity's direction component by sending some sort of "Direction Change Message" to the entity instead of referring to the Entity's component directly. This decouples the manager from needing to know how to change directions by using a message instead of relying on a specific component being there (which it may not be there at all, in which case the message would just fall on deaf ears instead of some error occurring because an expected object was absent).

You'll notice a huge advantage from this since you mentioned you have a Network Interface. A Network Component would listen to all messages coming in that everyone else should know about. It loves the gossip. Then when the Network System updates, the Network components send those messages to other Network Systems on other client machines, which then resend those messages to all the other components to update player positions, etc. Special logic might be needed so that only certain entities can send messages over the network but that's the beauty of the System, you can just have it control that logic by registering the right things to it.

In short:

Entity is a composition of Components that can receive messages. Entity can receive messages, delegating said messages to all of their components to update them. (Position changed Message, Speed Change Direction, etc) It's like a central mailbox that all components can hear from each other instead of talking directly to each other.

Component is a small part of an Entity that stores some state of the entity. These are able to parse certain messages and throw other messages out. For instance, a "Direction Component" would only care about "Direction Change Messages" but not "Position Change Messages". Components update their own state based on messages, and then update other components' states by sending messages from their System.

System manages all components of a certain type, and is responsible for updating said components each frame, as well as dispatching messages from the component's they manage to the Entities that the Components belong to

Systems could be able to update all of their components in parallel, and store all messages as they go. Then when execution of all Systems' update methods complete, you ask each system to dispatch their messages in a specific order. Controls first possibly, followed by Physics, followed by direction, position, rendering, etc. It matters which order they are dispatched in since a Physics Direction Change should Always out weigh a control based direction change.

Hope this helps. It's a hell of a Design Pattern, but it's ridiculously powerful if done right.

C0M37
  • 243
  • 2
  • 7
0

1) Your Factory method should be passed a reference to the EntityManager that called it (I'll use C# as an example):

delegate BaseEntity EntityFactory(EntityManager manager);

2) Have CreateEntity also receive an id (e.g. a string, integer, it's up to you) besides the class/type of the entity, and automatically register the created entity on a Dictionary using that id as a key:

class EntityManager
{
    // Rest of class omitted

    BaseEntity CreateEntity(string id, Type entityClass)
    {
        BaseEntity entity = factories[entityClass](this);
        registry.Add(id, entity);
        return entity;
    }

    Dictionary<Id, BaseEntity> registry;
}

3) Add a Getter to EntityManager to get any entity by ID:

class EntityManager
{
    // Rest of class omitted

    BaseEntity GetEntity(string id)
    {
        return registry[id];
    }
}

And that's all you need to reference any ComponentManager from within your Factory method. For instance:

BaseEntity CreateSomeSortOfEntity(EntityManager manager)
{
    // Create and configure entity
    BaseEntity entity = new BaseEntity();
    RenderComponent renderComponent = new RenderComponent();
    entity.AddComponent(renderComponent);

    // Get a reference to the render manager and register component
    RenderEntityManager renderer = manager.GetEntity("RenderEntityManager") as RenderEntityManager;
    if(renderer != null)
        renderer.Register(renderComponent)

    return entity;
}

Besides Id you can also use some sort of Type property (a custom enum, or just rely on the language's type system), and create a getter that returns all BaseEntities of a certain type.

David Gouveia
  • 24,875
  • 5
  • 84
  • 124
  • 1
    Not to be pedantic, but again… in a pure Entity (relational) system, entities have no type, except that imparted to them by virtue of their components… – BRPocock Dec 22 '11 at 21:22
  • @BRPocock: Could you create an example that follows the pure virtue? – Zolomon Mar 11 '12 at 11:11
  • @Zolomon an example is not necessary imho. An entity is determined not by its actual type but by its components. No BaseEntity, PlayerEntity classes etc, but exclusively an Entity class. If you're looking for "blueprints" of entities, I'd suggest what I call Prototypes - which is just a collection of components that can be added to an Entity. – raine Mar 11 '12 at 15:15
  • @Raine That's the hybrid approach, not what he meant by pure entity system. In a pure entity system there is not even an Entity class, only Components. Components have an EntityId and are usually stored together by type. An entity is then defined as the collection of all Components sharing the same EntityId. This sort of implementation has its benefits such as increasing cache coherency, but also has its share of cons - e.g. it's harder to implement communication between entities because there's no central "hub", and harder to debug too because you can't just watch an entire entity. – David Gouveia Mar 11 '12 at 15:41
  • And to wrap it up, there are many ways to implement a game object model, each with its pros and cons. There's the classic object based system which relies on class hierarchies. Changing inheritance into composition gives rise to the component based system. These may use a central entity class to hold components, or components can be stored separately in which case the entity is implicit (i.e. pure). Both are perfectly valid approaches. There's even a pure property based system where entities are simply collections of data, e.g. with behavior being added by attaching a script file. – David Gouveia Mar 11 '12 at 15:58
  • Cache coherence? I think the time spent chasing entity id's far outweights any cache gain whatsoever. re the "hybrid" approach, it is hybrid depending on the kind of standard you follow, and as far as entity systems go, there ain't any. but your assessment is correct, of course. – raine Mar 11 '12 at 16:00
  • 1
    @Raine Perhaps, I don't have first hand experience with this, but that's what I read. And there are optimizations you can implement to reduce the time spent looking up components by id. As for cache coherence, I think it makes sense since you're storing data of the same type contiguously in memory, especially when your components are lightweight or simple properties. I've read that a single cache miss on the PS3 can be as expensive as a thousand CPU instructions, and this approach of storing data of similar type contiguously is a very common optimization technique in modern game development. – David Gouveia Mar 11 '12 at 16:16
  • @DavidGouveia that is true, cache coherence is mentioned everywhere as the first thing to take care of when optimizing for modern architectures. The systems that take advantage of this are however fundamentally different than message-driven systems. in a message driven system, with components being glorified, stateful message handlers, you can't just cycle over components. only some components are activated, and often just one entity is involved in the processing. just another way of tackling entity systems. – raine Mar 11 '12 at 16:33
  • 2
    In ref: “pure” entity system: the Entity ID is typically something like: typedef unsigned long long int EntityID;; the ideal is, that each System can live on a separate CPU or host, and only require to fetch components that are relevant to / active in that System. With an Entity object, one might have to instantiate the same Entity object on each host, making scaling more difficult. A pure entity-component-system model splits processing across nodes (processes, CPUs, or hosts) by system, rather than by entity, typically. – BRPocock Mar 12 '12 at 21:11
  • 1
    @DavidGouveia mentioned “optimizations…looking up entities by ID.” In fact, the (few) systems I've implemented this way, tend to not do so. More often, select Components by some pattern indicating that they're of interest to a particular System, using Entities (ID's) only for cross-component Joins. – BRPocock Mar 12 '12 at 21:12
  • Minor coding note: you don't go randomly "chasing entity id's" while processing in a System; each System should be built to get any info from those id's it needs before diving deep into its tight, cache-happy loops. So +1 to all you guys who grok this kind of setup =) – Patrick Hughes May 11 '12 at 01:24
0

I'm using a similar system in my engine and the way I've done it is each Entity contains a list of Components. From the EntityManager, I can query each of the Entities and see which ones contain a given Component. Example:

class Component
{
    private uint ID;
    // etc...
}

class Entity
{
    List<Component> Components;
    // etc...
    public bool Contains(Type type)
    {
        foreach(Component comp in Components)
        {
            if(typeof(comp) == type)
                return true;
        }
        return false;
    }
}

class EntityManager
{
    List<Entity> Entities;
    // etc...
    public List<Entity> GetEntitiesOfType(Type type)
    {
        List<Entity> results = new List<Entity>();
        foreach(Entity entity in Entities)
        {
            if(entity.Contains(type))
                results.Add(entity);
        }
        return results;
    }
}

Obviously this isn't the exact code (you'd actually need a template function to check for different component types, rather than using typeof) but the concept is there. Then you could take those results, reference the component you're looking for, and register it with your factory. This prevents any direct coupling between Components and their managers.

Mike Cluck
  • 1,274
  • 1
  • 11
  • 23
  • 3
    Caveat emptor: at the point at which your Entity contains data, it's an object, not an entity… One loses most of the paralelizing (sic?) benefits of ECS in this structure. "Pure" E/C/S systems are relational, not object-oriented … Not that it's necessarily "bad" for some case(s), but it's certainly "breaking the relational model" – BRPocock Dec 22 '11 at 21:21
  • 2
    I'm not sure I understand you. My understanding (and please correct me if I'm wrong) is the basic Entity-Component-System has an Entity class which houses Components and might have an ID, name, or some identifier. I think we may have a misunderstanding in what I mean by "type" of Entity. When I say Entity "type," I'm referring to the types of Component. That is to say, an Entity is a "Sprite" type if it contains a Sprite Component. – Mike Cluck Dec 23 '11 at 00:28
  • 1
    In a pure Entity/Component system, an Entity is usually atomic: e.g. typedef long long int Entity; a Component is a record (it might be implemented as an object class, or just a struct) that has a reference to the Entity to which it's attached; and a System would be a method or collection of methods. The ECS model isn't very compatible with the OOP model, although a Component can be a (mostly) data-only Object, and a System a code-only singleton Object whose state lives in components... although "hybrid" systems are more common than "pure" ones, they lose many of the innate benefits. – BRPocock Dec 23 '11 at 02:44
  • (Sorry for the encyclopædic comments, but:) the problem with a “type” is that an Entity is inherently of any type for which it holds a Component, so you can do GetEntitiesOfType by simply iterating a global list per-Component, which might be on a different host than another Component. The model is relational, like SQL tables — Components map nicely to TABLEs, Entities to PRIMARY KEYs. – BRPocock Dec 23 '11 at 02:48
  • I see what you mean and I appreciate the extensive information. It seems I've only ever experienced hybrids. Time to do some reading :) – Mike Cluck Dec 23 '11 at 05:24
  • There's a pretty good overview series at http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/ et seq. – BRPocock Dec 23 '11 at 14:32
  • 2
    @BRPocock re "pure" entity systems. I think an entity as an object is perfectly fine, it doesn't have to be a simple id. One thing is the serialized representation, another the in-memory layout of an object / a concept / an entity. As long as you can maintain data-drivenness, one shouldn't be tied to non-idiomatic code just because it's the "pure" way. – raine Mar 11 '12 at 15:21
  • @Raine, in the case of having data inside an Entity Object, however, one easily falls prey to having to instantiate the object with a cluster of weak references (or similar) to every possible component, which can seriously harm scalability of the system – BRPocock Mar 12 '12 at 21:13
  • 1
    @BRPocock this is a fair warning, but for "t-machine"-like entity systems. I understand why, but those are not the only ways to model component-based entities. actors are an interesting alternative. I tend to appreciate them more, especially for purely logical entities. – raine Mar 12 '12 at 21:55
  • The trick is to first forget anything you ever knew about classical OOP designs. – Patrick Hughes May 11 '12 at 01:18
  • @BRPocock what do you mean by "one easily falls prey to having to instantiate the object with a cluster of weak references" ? – Luke B. Nov 06 '12 at 17:46
  • If an Entity is a class of OOP object, then typically it has to hold together its Components. This causes all sorts of “fun problems,” particularly with regards to distributed processing, multiple threads, and the like. EG: Entity.GetComponentTypeXYZ() might have to load the component data, or else every component of every entity must be visible in core somehow. – BRPocock Nov 06 '12 at 19:01