7
\$\begingroup\$

Using C# and XNA 4, I've made the decision to go for an entity-component based design for my game after reading such posts as this and this, but I'm now struggling to find how to retrieve components once they've been added to an entity.

Some articles I've read say that when a component is created, register it with the system that will act upon it, using a messaging system. A drawback I see here is that if a system needs 2 components to act (say CollideComponent and PositionComponent) then this gets complicated (and I can't figure how it would work).

The other possibility, and the one that I'm currently pursuing, is to have a system ask whether an entity has a particular component when it needs it. However I've not seen any code examples, only theory on the net, so I've been trying to muddle it together. Some code here might explain how far I've got :

First a System:

public class RenderingSystem : IGameSystem
{
  public void PerformAction(IQueryable<IGameObject> gameObjects)
  {
    foreach (var gameObject in gameObjects)
    {
      RenderableGameComponent renderableGameComponent = gameObject.GetComponent<RenderableGameComponent>(ComponentType.Renderable);
      if (renderableGameComponent != null)
      {
        this.spriteBatch.Draw(renderableGameComponent.Texture, renderableGameComponent.DrawablePosition, Color.White);
      }
    }
  }
}

Now the actual GameObject:

public class GameObject : IGameObject
{
    private List<IGameComponent> gameComponents;
    private ComponentType componentTypes;

    public void AddComponent(IGameComponent component)
    {
        this.gameComponents.Add(component);
        this.componentTypes = this.componentTypes | component.ComponentType;
    }

    public T GetComponent<T>(ComponentType componentType)
    {
        return (T)this.gameComponents.FirstOrDefault(gc => gc.ComponentType == componentType);
    }
}

And finally the Component and ComponentType enumeration :

public class RenderableGameComponent : IGameComponent
{
    public Texture2D Texture { get; set; }

    public Vector2 DrawablePosition { get; set; }

    public ComponentType ComponentType
    {
        get { return ComponentType.Renderable; }
    }
}

[Flags]
public enum ComponentType
{
    Renderable = 1,
    Updateable = 2,
    Position = 4,
}

Now as you might be able to see, I'm trying to use bitwise operations on the ComponentType enumeration that every Component of each GameObject has. The reason being I thought this would be a much cleaner approach when trying to get the required Component out of the GameObject when asked by a System (also refer to link 1 above).

I'm trying to get away from passing in a type, and doing an iteration over all the Components of a GameObject. eg:

foreach (var component in this.GameComponents)
{
  if (component.GetType() == requestedType) { return component; }
}

This is because when there are a few hundred bullets on the screen and a CollidableSystem is created, it will massively slow the game down checking every type. I've had experience with this when creating a previous game, but admittedly, that was using an inheritance based design system.

It worries me that for each System that is created, it will have to iterate over every GameObject every tick asking the same question every time, "Do you have the component I am looking for ?" and still doing a lot of casting and null checks.

Now my question is this. I want to know whether there is a de facto design pattern for getting Components from GameObjects that don't require null checks or casting ? Someway a System will say "Give me this component, I know that you have it" and not bother wasting time asking those GameObjects that don't have the Component. Tell, don't ask.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I don't know if there is a de facto design pattern but one example is the Artemis framework.(gamadu.com/artemis) In this framework each system knows the component types it is interested in and is notified when an entity is added or removed from the "world" to which it is assigned. In other words, each system will only process those entities that have the components matching it's criteria. \$\endgroup\$ Jan 3, 2013 at 22:35
  • \$\begingroup\$ I've had a play around with the Artemis Framework, but this is still performing type checking. It effectively uses a repository pattern for each type of component, checks what type a component is and adds it to the correct repository for retrieval later. \$\endgroup\$
    – David K
    Jan 4, 2013 at 9:51

4 Answers 4

3
\$\begingroup\$

Systems maintain a list of the entities they're interested in.

Systems are just created once when the game is initialized. If you're creating systems with entities already in play, you're doing it wrong :).

All the systems your game will use are to be created before any entities are created. When a new entity is created, each system will check to see if it's interested in that entity for processing. This is where your bitwise check comes into play, very fast. When an entities components change, each system should check that entity again to see if it's still interested in processing it. See an example of this check in the Artemis Framework code with the check(Entity e) function.

Now, when each system processes, it just iterates over the list of entities it has stored.

Retrieving components is done with nested hash maps. The first layer is a map of maps indexed by the component ID, likely the long used for bitwise identification would work well here. The second layer is a map of components indexed by the entity ID. This would look something like the following in tree format:

-Renderable (1)
  |-- Entity 1 (1)
       |-- Instance of Renderable
  |-- Entity 2 (2)
       |-- Instance of Renderable
-Updateable (2)
  |-- Entity 1 (1)
       |-- Instance of Updateable
-Position (4)
  |-- Entity 1 (1)
       |-- Instance of Position
  |-- Entity 2 (2)
       |-- Instance of Position

Where Entity 1 is an entity with Renderable, Updateable and Position components and Entity 2 is an entity with Renderable and Position components.

These hash maps have constant access times and are very easy to navigate.

So when processing entities you can easily retrieve a component with something like the following (similar to the Artemis method):

Component getComponent(Entity e, ComponentType type) {
       HashMap<Long, Component> components = componentsByType.get(type.getLongID());
       if(components != null) {
            return components.get(e.getID());
       }
       return null;
 }

There's still a null check, but you can get rid of that if you fill all your componentsByType maps with empty maps for each component. Enabling you to shorten the look up to:

 Component getComponent(Entity e, ComponentType type) {
       return componentsByType.get(type.getLongID()).get(e.getID());
 }

It's still possible this will return null if the entity doesn't have that component type. It's up to you if you want to check for that, but if the system added the entity based on its components, that component should be there, so it's up to you if you want to take that risk.

\$\endgroup\$
2
  • \$\begingroup\$ 'Systems maintain a list of the entities they're interested in.' Sometimes you just need it spelled out to you :) Thanks, I was missing this point! Would it be reasonable, however, for an EntityManager (or Engine) to maintain a list of these, and the systems themselves just have a reference to the list. That way they wouldn't have to have worry about new entity registration/removal to their list, and could delegate that task to the more generic EntityManager class. That class would then manage entities for all registered systems. \$\endgroup\$
    – David K
    Jan 8, 2013 at 8:44
  • \$\begingroup\$ The EntityManager could maintain those lists, but it may be easier for the system to do it. Since no one else but the system needs access to that list. Systems would not be processing when new entities are added, so you don't have to worry about systems being busy. If a thread adds a new entity while the systems are processing the entity should be added to an add list that gets integrated on the next frame, you don't want to run into concurrency issues with your entity list. \$\endgroup\$
    – House
    Jan 8, 2013 at 15:49
2
\$\begingroup\$

When I was thinking in the optimal way of getting components belonging to a specific entity I encounter the following main concern:

Speed: constant time vs lookup.

Now, this is game programming, if you game does not perform fast you have to address that somehow.

My first attempt was to store components in a dictionary, the dictionary ordered components by type id, the type id was a 64 bit unsigned integer and it was obtained for each component by calculating hash of its class name string. The problem is that the getComponent method of the Entity class involved a look up, probably binary search, but still not optimal when comparing to constant time. Then to achieve constant time access to components, I did as Richard Lord suggests in his blog post about entity systems, I created nodes inside systems that permit access to components through static typing, no lookup. With this method I only called the getComponent method of the Entity when adding each entity to systems, to check if required component were there, and if they were then I created the node. The node is an structure/class made of pointers to components. I only did this for critical systems such as collision detection or rendering, not for AI for example, because AI may accept multiple components sets, and take different code paths depending of presence or state of components, so AI still used getComponent a lot.

Later, I found this and this. Good optimizations, because if you can access components fast and in constant time, you don't need nodes at all. With the corresponding speed and memory benefits.

Actually what I do is to store components in a vector. Each component has an unique index, not a type id like before. The assignation of index values for each type is sequential. I won't enter in details on how I achieved this, for now you can go with an enum (similar to what you are already doing).

I have two versions of getComponent function, one check for inexistent components (used by systems that can act in a variety of entities even if not all have the same component set, like AI), the other, the fast one, allow memory violation if you try to access an inexistent component. System that access components this way, have an interface to get notified when an entity changes its component set (removal or addition of components) and then recheck the entity and if it has no more the required components by that system the entity get removed from the system, but these systems never check for components during game loop, because that would make them run slow.

With my suggestion of use a vector and not a dictionary, you may run in cases where an entity only has lets say the Position component that has an index value of 0, and the CharacterStatus component that has and index value of 64 (actually less but let use a high number to illustrate this situation), as we are using a vector to store components ordered by index, then this mean that you end with a vector with 65 items, and item 1 to 63 are null pointers. To minimize this impact you will try to assign high index values to less used components. I have do some calculation (considering only the size of the entities internal vector) of the memory cost of the worst of cases (this is when you have all entities containing the last component, the one with the higher index value) and the number of entities that I can create is still in the tens of thousands and estimated memory used by entities is still acceptable comparing to the benefit of accessing components in constant time.

You have to get rid of the dictionary because access to its items involves a look up. You have to make retrieval of components constant time, or use nodes as Richard Lord shows in his post.

Note: I'm using C++, not C#, but all I have said is language agnostic. For example, I used std::map as dictionary in my first attempt. But you get the idea. In C# you can use List from System.Collections.Generic instead of std::vector, it will allow you to access components references in constant time.

Finally: The first link from entity systems wiki suggests to store components in vectors, one for each component type. I think that this is not friendly to C#, because this

List<ComponentClassName>

would mean a sequential list of references to ComponentClassName and whatever you do you get a reference, then do that in C# don't have sense, something similar as what I'm doing is better for C# (each entity have a vector of pointers, well references in your case, to components, static casts are still needed). In C++ you can do std::vector and that would be different than std::vector (with the * at the end) so the method suggested in the wiki may still have some sense in C++, maybe it requires even less memory than what I'm doing.

You still may prefer the wiki suggested method as you stated that you are concerned about doing casts constantly. With one vector (List) per component type, and the entity being only an integer, you don't need casts at all. Something like:

Vector3 displacement = position_list[entity_index] - previous_position_list[entity_index];

Assuming the "laziest" of implementations where you expose the lists to the systems.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for the link to Richard Lords post. Along with your answer, that has has seriously helped my understanding. I now know that I was missing an EntityManager (or Engine) class and was trying to do everything (wrong) within the game loop between the systems and entities directly. \$\endgroup\$
    – David K
    Jan 8, 2013 at 8:39
  • \$\begingroup\$ Is not that you did something wrong. There are a lot of strategies to get something to work. The reason I want you to think in these "optimizations" from the beginning is because they may require some small changes in your interfaces. Think in this now may save time later. Fast access to components is key, I think. \$\endgroup\$ Jan 8, 2013 at 23:18
  • \$\begingroup\$ Sorry for the /very/ late comment. Was there any reason you couldn't use std::unnordered_map ? \$\endgroup\$
    – Veritas
    May 28, 2014 at 19:31
  • \$\begingroup\$ std::unordered_map is faster than std::map and at the same time saves you from having all those wasted empty pointers, they are still slower than vector I think, so you will have to decide what is more important, a bit more of speed or save memory. Note that I switched to another approach. Now my entities are only an ID and components are singletons that can return their data if you pass them an entity ID (not so efficient as lookups are involved) or let you iterate through all the entities that are associated to that component (what systems do most of the time, and very efficient). \$\endgroup\$ May 30, 2014 at 16:23
0
\$\begingroup\$

I had the same concern, what I did was to have a Dictionary, each key is a ComponentType, and the value is a list of GameObjects with components of that type.

Dictionary<ComponentType, List<GameObject>>

That way most of your software design remains unchanged, the system asks to the dictionary only what he need, no unnecessary iterations and no null checks.

The other way I wanted to answer your question is well explained by Ryan Maloney in his comment.

\$\endgroup\$
0
\$\begingroup\$

Just complementing what Ryan Maloney told. I worked on the port of the amazing Artemis Entity system (Java Version: http://gamadu.com/artemis/ and our C# version http://thelinuxlich.github.com/artemis_CSharp/).

Systems are "somehow "conected with Components, when you create a system you specify the types of components that it process and at runtime only the entities that has those components types are send to the System for processing. When you remove or add a Component of an entity (we call that the entity was refreshed), the interested systems are notified (their method onChange will be called).

I really recomend you to take a look at the source code https://github.com/thelinuxlich/artemis_CSharp

Most of the problems you are talking about were "solved" there =P

There is also a basic demo game using it here https://github.com/thelinuxlich/starwarrior_CSharp

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .