ES Architecture.

NOTE: This blog post is a work in progress, which I am keeping visible because it may be of some use even before finishing.

Entity/System Architecture is causing a little buzz on the internet.  Some hackish form of it is already being employed in most games and game engines, according to Megan Fox (no, not that one…).

If you want to know how it works, the best quick intro I’ve found (for not-so-badass programmers like myself) is this article on the model as conceived, and this follow-up article on the model as modified and implemented in the Java Artemis framework.  Once you’ve read those two articles for the introduction, read the foundational essays by Adam Martin that inspired the Artemis framework.  Martin also has a wiki devoted to ES, the URL of which has escaped me for the moment, but you will get much farther by reading his essays on T=Machine.

If you search Amazon for books on ES game programming, you will find the literature just isn’t there.  If you Google it and surf around you will find a lot of pages that give overviews of varying quality.  The concept is really old, but it would seem it is just starting to get its own head of steam.

ES sounds like a dream come true to a budding indie, especially one whose strengths are not coding, but art and story and such.

For a long definition, check out the links in the order I posted them: first Fox’s, to see how it’s normally done, then the piemaster articles to learn the difference between the hackish standard technique and the more purist technique used in Artemis and in Martin’s work.  Then, if you like it, read Martin’s stuff.

K.  Short description:

Forget Object Oriented architecture.

Game programs are made of three things:

Systems do the work of the program… e.g. there is a Drawing System and a Physics System and so forth.

Components are clumps of data upon which systems work.  For instance, Drawing System needs a Position component (to know where to draw) and a Graphic component (to know what to draw).  A Physics system needs a Velocity component (to know how far to move) and a Position component (to know where to move from; also to write the result of the move).  Because multiple systems may need access to the same component (for instance both Physics and Drawing need to access Position), Components are abstracted away from Systems.

Think of systems as basically Turing machines; tape recorders working their way down a strip of tape.

Great, so if components are strips of tape, how do you know which position goes with which graphic?

Entities are names (well, really IDs).  Two components with the same name go together.

You can also picture it as a graph or table.  Each column is a Component type, each row is an Entity.  Each System rolls down the table, and if any given row has Components that System requires (such as a Graphic and a Position), the system stops and processes those components.

(And in fact, that is roughly how Artemis operates).

The upshot of all this is that you can add any behavior to any entity merely by adding the necessary components.  Want to turn a rock in your game from a chunk of static background to a chunk of destructible background?  Just add a Health component.  Bam!  Almost zero extra coding.

That’s a bad summary, but the internet is full of summaries which detail more advantages (and disadvantages).  The ones I’ve linked do a good job of defining my target.

The question is how do I get as close to my target as possible while slapping something together in C# and XNA and understanding everything I’m doing and having it up and running before I go to bed in a couple of hours and on and on and on.

Here’s my proposed architecture:

1. Components:  A Component is just data.  But every component has: 1) An int IDing the component’s Entity, and 2) A flag representing whether the component is attached to an entity or not (thus allowing us to detach components by setting the flag).  These may be the same data point (i.e. reserve one entity ID for “N/A”).

2. Component Manager: Contains a fixed-size array of one type of component.  Has a unique integer ID.  Maybe has some efficiency-related stuff (calls to add or remove components; pointers to the current earliest unclaimed component so we can claim/reclaim it; a pointer to the current last component so we don’t have to loop through the entire array unless it’s actually full).

3. Entity: Contains a string name for human reference, and an array of ints equal in size to the number of possible components, with the variable at the index equal to the ID of a given manager is either -1 (this Entity does not contain that component) or else the index of an attached component in its manager’s component array.

4. Entity Manager: This is important:  Entities are not Entity Objects.  Entities are numbers.  Integers used as names.  The Entity Manager has an array of Entity Objects.  That array is nothing more than a lookup table, allowing us to find components attached to the Entity that is the array’s index.

Hence, to attach a Graphic component to an Entity, (given OurHero as an int)  you might say…

EntityManager.Entities[OurHero].Components[GraphicManager.ID] = GraphicManager.Add(HeroTexture);

GraphicManager.Add would find an unclaimed Graphic in its array, claim it, load HeroTexture into it, and return the index of the Graphic.  Thus, the EntityManager.Entities array can serve as a look-up table in case we have something that may need to modify the Graphic (say an Animation system).

Which brings us to…

5. System:  A system does not crawl through every entity (that would be lame.  Most entities are going to be particles, for instance, and we don’t want to make the Enemy AI system look up a crap ton of — say — smoke particles that make absolutely no difference to enemy AI).  Instead, every system has one component type that is crucial.  One type that implies the system.  A Position component may be used by any number of systems, but a Graphic component implies a Drawing System.

The System only loops through the array of the component that implies it (i.e. DrawingSystem only loops through GraphicManager’s array), but it takes as arguments the managers of the other components it needs to operate (such as Position).  Then, when it processes that component, it check’s the EntityManager to get the indices of the other components it needs.  E.g.

int p = EntityManager.Entities[GraphicsManager.Graphics[i].Entity].Components[PositionManager.ID];
if (p > -1 && p < PositionManager.Length) Draw(GraphicsManager.Graphics[i], PositionManager.Position[p]);

These indices are pre-validated.  If the System doesn’t have access to all the components it needs, it simply skips that component.

So, the Entity/EntityManager, once again, does not maintain Entities; it merely maintains a look-up table.

That’s the plan.  Let’s see how it goes.

Step 1:  Entity.

using System;
namespace AtomSmasher
{
   public class Entity
   {
      public int[] Components;
      public bool Live = false;
      public Entity(int NumberOfComponents = 1)
      {
         Components = new int[NumberOfComponents];
         KillEntity();
      }
      public void KillEntity()
      {
         for (int i = 0; i < Components.Length; i++) 
             Components[i] = -1;
         Live = false;
      }
   }
}

Yeah… Atom Smasher.  It seemed the logical choice of names for an ES engine…

Note the lack of stuff in Entity.  Hell, I even stripped out the Name — we can attach the name as a Component.

K, now Components.

using System;
namespace AtomSmasher
{
  public virtual class Component
  {
    public int EntityID = -1;
  }
}

Yeah.  Most stuff will be implemented in the individual components.  ‘Nuff said.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: