Entity System question

Started by
28 comments, last by Kylotan 15 years, 11 months ago
Hello everybody, I am currently developing my first own "larger" game. It should once become a simple 2D space side-scrolling game. I want it to be extensible and quite reusable for later projects. Till now I have only programmed simple games like Tetris and such things. Now I want to implement a more complex one. I have never before thought much about game design but now I have to because I want to have my code a little bit more organized. I am reading a bit on Component Oriented Programming and Entity Systems (like here or here). I like it very much so far and want to implement my game more or less like this. But there are some things that are not clear to me and it would be nice if you guys could help me a bit! I'm from Switzerland and my first language is not English. I hope you understand me well. ;) So here is how I'm planning to organize my game: All the game objects are single entities that consist of components. The entities can use components to form a certain object in the game. Examples of components are: Position, Render, Physics, Controller, Life. The implementations of the components are not in the component itself, they are in a separate subsystem (e.g. the Render() function of the Render component is in the Rendering Subsystem). In the components I store only specific data needed by the component (e.g. texture name for the Render component). All this sounds quite good, but which class has to know about the existence of which class? My idea was to register a component in its subsystem when it's created and remove it when it's deleted. So no component knows about each other, but each subsystem has a list of pointers to the components that belong to the system. This would look something like this:
/*--------ENTITY---------*/
class Entity
{
public:
   Entity( const ObjIdType& rId) {
      setID(rId);
   }

   const ObjIdType& getID() const { return m_OID; }
   void setID( const ObjIdType& rId ) { m_OID = rId; }

   Component* GetComponent(const CompIdType& rFamilyID ) {
      return m_components[rFamilyID];
   }
   Component* SetComponent(Component *pNewComp);
   void ClearComponents();

private:
   ObjIdType m_OID; /*unique identifier for this object*/

   std::map<const CompIdType, Component*> m_components; /* map of all components */
};

/*--------COMPONENT--------*/
class Component {
public:
   Component() : m_pOwner(NULL) { /* register comp in subsystem */ }
   virtual ~Component() = 0 {}

   virtual const CompIdType& ComponentID() const = 0;
   virtual const CompIdType& FamilyID() const = 0;

   void SetOwnerObject( Entity* obj ) { m_pOwner = obj; }
   Entity* GetOwnerObject() const { return m_pOwner; }
private:
   Entity* m_pOwner;
};

/*--------SUBSYSTEM---------*/
class SubSystem {
public:
   void RegisterComponent( Component* );
   void UnregisterComponent( Component* );
   virtual void Update() {}
private:
   std::vector<Component*> m_components; /* map of all components in the system */
};

/*-------THIS IS AN EXAMPLE OF A COMPONENT------*/
class CompRenderPolygon : public Component
{
public:
   CompRenderPolygon() { RenderSystem::RegisterComponent( this ); }
   ~CompRenderPolygon() { RenderSystem::UnregisterComponent( this ); }

   SetTexture(...);
   SetColor(...);
   SetTransparency(...);
private:
   /* data */
}






What I like in this component system is that I can have nice data-driven programming. What I'm not sure about is how the Entities or Components are interacting with each other (with events?). Here an example: I have 3 entities, a player, a monster and a custom visual effect. When the player kills the monster (ID "monster12") I want the visual effect to be deactivated. How would I do this? The Life component of the monster knows that he's dead, but how would it know it has to pass this information to the visual effect? Does the Life Subsystem send a message to all other components in the world? (like "monster12 has been killed by player") Or does the monster know that when it dies it has to pass the "deactivate" message directly to the visual effect? It would be nice if you could explain me how you would do this event handling. (solving the example) Maybe you could present your own manner to handle this. Comments on my "Entity System" are also very welcome. Thank you for reading this long post. I'm pretty sure I have left something unclear, please just ask when something is like that. :) Thanks in advance, I'm looking forward to your answers! Aeroz [Edited by - aeroz on April 8, 2008 3:53:49 AM]
Advertisement
Quote:
What I'm not sure about is how the Entities or Components are interacting with each other (with events?). Here an example:
I have 3 entities, a player, a monster and a custom visual effect. When the player kills the monster (ID "monster12") I want the visual effect to be deactivated. How would I do this? The Life component of the monster knows that he's dead, but how would it know it has to pass this information to the visual effect?


Your SubSystems should be responsible for creating events and receiving events. The individual components do not send the events, but they create them (e.g. Life component sees entity.Life = 0, so it creates an EntityDeadEvent) and then they give them to their parent SubSystem. The SubSystem will receive the events from all of its child components and will then send the events out to the other SubSystems.

When a SubSystem receives an event from another SubSystem (e.g. LifeSubSystem sends event to VisualEffectSubSystem) it could pass the event on to all of its child components who care to receive the event. For example, the VisualEffectSubSystem receives an event from the LifeSubSystem informing it that "entity12" has 0 Life. The VisualEffectSubSystem could either destroy its child component attached to "entity12," without the CustomVisualEffectComponent knowing why, or it could forward the event to the component and the component can then see that its entity has 0 Life and destroy itself. Either way, the CustomVisualEffectComponent attached to "entity12" gets destroyed/removed.

Quote:
Does the Life Subsystem send a message to all other components in the world?


No. The Life Subsystem sends an event to all other Subsystems. The receiving Subsystem then chooses what to do with the event.

Quote:
Or does the monster know that when it dies it has to pass the "deactivate" message directly to the visual effect?


Definitely not. This creates unnecessary coupling between each monster and its visual effect component. What if you want more than one visual effect component? The monster would have to tell both of them.

If instead the LifeSubSystem sends an event to the VisualEffectSubSystem informing it that the entity's Life = 0, the visual effects components attached to that entity can just kill themselves, and there is no need to pass information between the entity and its visual effect components. This would allow you to attach any number of visual effect components to an entity without the entity knowing, and when the entity dies the visual effect components destroy themselves.
Shakedown: Very helpfull reply, thank you!

Quote:The individual components do not send the events, but they create them (e.g. Life component sees entity.Life = 0, so it creates an EntityDeadEvent) and then they give them to their parent SubSystem.
Actually I have no methods in the components (only getter and setters), so the component would not know by itself that it's dead. The LifeSubSystem would check if the child component is dead and then create the event. I don't see how a component can create an event on its own since there is no Update() method in the component.

Quote:When a SubSystem receives an event from another SubSystem (e.g. LifeSubSystem sends event to VisualEffectSubSystem) it could pass the event on to all of its child components who care to receive the event.
How does the SubSystem know which child components care about the event?


Then a more general question about this approach:
Since almost every time something is happening, wouldn't it be too much events going around the subsystems? For example: when the player has lost some health points there would be en event EntityLostHealthEvent going to every SubSystem. There would be quite a lot of such events.
What if I would like to know who killed the player? The event needs to be preciser and tell who has killed the entity and maybe also where the entity has been killed.

Aeroz
I'm personaly a fan of the get-the-job-done entity system. Where there isn't constant RTTI type stuff going on and that the objects type is well known by the object that is accessing it.

I think games should be built with a strict purpose with a strong class heirarchy. The entity object should only be the base, not some manifestor of poo.

In other words, you shouldn't be able to recompose the entity once you have it. You should have been passed it at the proper interface level. And the entity itself only represents the common factors.

Currently in my system this envolves starting, living, and ending. I also, since this is a 3d system, have determined that all entitys posses a 4x4 matrix for it's location. (this greatly fascilitates loading and saving)

My renderer however, is completely independent of entitys (which are parallel to your "components"). It is a unique system of renderables and scenes. There is no common relation to the entity so that pattern is not used at all.

I guess what i'm getting at is don't try to boil it all down into systems and subsystems and this and that. Just build it. And build it efficiently as you go.

"I have 3 entities, a player, a monster and a custom visual effect."

Those are 3 very different things. Don't try to boil them in the same pot.

/me hugs c# re RTTI
Hi bzroom,
Quote:I'm personaly a fan of the get-the-job-done entity system.
What do you exactly mean by "get-the-job-done entity system"? You mean a more Object Oriented way?

What I'm not sure about is whether I should implement all the functions into SubSystems or directly into the components. The last one seems easier to code but then there is a lot of memory duplication since there would be multiple copies of a component. Then I could easily do things like
World.GetEntity("Entity12")->GetComponent("Life")->DoDamage(100);
istead of sending events.
(of course I would have to check whether the entities and components exist and static_cast them) I have kind of a bad feeling doing this since I'm not sure this is a good way.
You said you don't like RTTI, but how do you know the Object types in your system? You don't have components in your system?

Quote:"I have 3 entities, a player, a monster and a custom visual effect."

Those are 3 very different things. Don't try to boil them in the same pot.
That's why they are 3 different entities. The player has let's say a Renderable, a Physics, a Life and an InputController component. The monster has the same but instead of an InputController he has an AiController. The visual effect entity has only a Renderable component. What I could have done bether is to put the visual effect component directly into the monster entity. When the monster dies the effect disapear also.

Aeroz
The system you describe sounds almost exactly like Sneftel's outboard component-based entity system architecture, so you could possibly get some inspiration from there.

I gave up on component-based entity systems midway when I realized that the complexity just wasn't worth it for the simple system I was evolving, but it sure is nice in theory - especially a system like what's been described for Dungeon Siege, the poster child of component driven architecture. The problem you describe is not unique to component systems however, it seems like a more general entity communication problem. From reading posts on this topic, I understand that one common solution for this is a global message queue. If only subsystems send and receive messages, this then becomes what Shakedown has described already.

If you want to be notified the moment a component has for instance died, you could use the observer pattern. When a component setter is called, if a condition is reached, raise an event on the listener (in this case the subsystem), which would then proceed to enqueue a message for other subsystems. This way you do not need an update function, and this functionality can probably be encapsulated in the base class of all components.

I have allready read the post you mentioned ("outboard component-based entity system architecture"). The problem is I don't understand exactly how he handles events there. Well, I'm a bit new to this sort of design, so I'm just going to try something out. Although it would be nice if you would show an example of how you solved this problem in your engine/game.
Quote:I gave up on component-based entity systems midway when I realized that the complexity just wasn't worth it for the simple system I was evolving
I'm not sure whether I should go for the component system, but it realy sounds good in theory!

Wouldn't it be a good solution to have an EventHandler class that takes care of distributing the events to the correct comopnents?
For example in the LifeSubSystem:
if( component->GetLife() < 0 ){   Event* DeadEvent = CreateDeadEvent();   EventHandler::NewEvent( DeadEvent ); /* send event to EventHandler */   component->GetOwnerObject()->Destroy(); /* because the entity is now dead */}

The EventHandler sends the Event to the components(or other type of objects?) that are waiting for the DeadEvent (they are registered in the EventHandler).

Just still a small question: what are exactly zones? (For example a zone in my level that when the player enters it, a message pops up: "You entered the enemy's base! Try to find the hidden star as fast as possible!") Is a zone an entity with a zone component? Does the zone component then check all other entities that have a position component whether they are in the zone and if yes send an EntityEnteredZoneEvent?

Thank you for the replies!
Aeroz
Quote:Original post by aeroz
Although it would be nice if you would show an example of how you solved this problem in your engine/game.

You're giving me too much credit, I wouldn't exactly say that I ended up solving anything. Take everything I say with a grain of salt or two :D

If I were to classify my system based on the T=Machine article you linked, it would be number 5 - excessive use of observer pattern. I had an entity class (first red flag right here) which had a list of components and could be queried for a component given a type enum (cheap man's RTTI). Components talked to other components directly by registering listeners, or by querying their parent entity for a specific component type. Subsystems got lists of components to process each turn (for instance, Renderer got a list of Renderables in the current PVS).

So in retrospect, my design was a mess - first time writing a component system. I gave up on it for mostly the same reason that I gave up on excessive scripting - in a one man, single-player project, clarity and ease of development beats flexibility - but I'm not yet sure where I'll go from here yet. This components-owned-by-subsystems seems promising, but also quite a bit more complicated to get a grasp on since entities are broken apart and dispersed in such a manner throughout the engine.

The whole data-driven programming is nice in theory, but ask yourself also what real benefit you get from it if you're the only developer and if there's no level designers to take advantage of this feature.

The Zone issue you mention sounds like something you could work out through collision detection with some custom trigger painted over the area (think Neverwinter Nights triggers). I would think it's an entity with a Collidable and some kind of Scriptable/Triggers component, but that's something that depends on your component granularity. So the collision response could send a message to trigger "onEnter", and onEnter would send a message to the player UI.
Thx lightbringer, I'll think about it a litte bit.
Quote:in a one man, single-player project, clarity and ease of development beats flexibility
agreed
Quote:onEnter would send a message to the player UI
What exactly is the player UI? A SubSystem?
Do you have on each frame an update of all SubSystems?
like:
void MainLoop(){   PhysicsSubSystem::Update();   InputSubSystem::Update();   AiSubSystem::Update();   LifeSubSystem::Update();   SoundSubSystem::Update();   ...   RenderSubSystem::Update();}


I'll come back later in a few hours.

Aeroz
Just updating subsystems like that may not be what you want to do. Sometimes, things like physics (or maybe logic also) would need to be decoupled from the framerate and called with fixed timesteps. I do implement UI as just another kind of system (in fact, it's another kind of renderer, one that gets called to paint its widget tree to screen in immediate mode OpenGL after the scene renderer is done).

This topic is closed to new replies.

Advertisement