Jump to content
  • Advertisement
Sign in to follow this  
Kain5056

Help understanding Component-Entity systems.

This topic is 1362 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to understand how Component-Entity systems work, but the more I read, the more confused I get. Every article I read does things very differently, and, most of the time, is too complicated for my level of experience to follow. So I'd like some pointers from someone more experienced. Maybe I'm not reading the right articles or tutorials for my case, so you may need to point me to a good source.

 

I'm very inexperienced, so try to not cringe if I write something too insane. '^^

 

This is what confuses me the most right now: I see many articles saying that an entity is nothing more that an integer id. That integer id determines what components the entity will have, with each component having its own id.

 

As I understand it, an entity is, for an oversimplified example, the id number 125, which means it contains the components with the id numbers 1, 2 and 5.

 

It's obvious that I understanding completely wrong. For example, what determines the stats of the entity? What determines the external files (spritesheets etc) that the entity uses? What determines if the entity is alive or dead? What determines the entity's stats and position? Can I get all that from just a number?

 

So, here's an example: I have a simple set of components, named "message" and "name", inheriting from a base component class. Each component reads a string from an external file. And I also have an entity struct that only has an int named "id". Can someone please explain how do I connect the components to the entity? Here's some oversimplified, probably atrocious code:

struct entity { int id; };

class component
{
public:
    int id = 0;
    void load( std::string file );
    void run();
};

void component::load( std::string file ) {}
void component::run() {}

class message : public component
{
public:
    int id = 1;
    std::string words;
    void show_message();
    void load( std::string file );
    void run();
};

void message::show_message() { std::cout << words << std::endl; }

void message::load( std::string file )
{
    std::ifstream infile( file.c_str() );
    std::getline( infile , words );
}

void message::run() { show_message(); }

class name : public component
{
public:
    int id = 2;
    std::string Name;
    void show_name();
    void load( std::string file );
    void run();
};

void name::show_name() { std::cout << Name << ": "; }

void name::load( std::string file )
{
    std::ifstream infile( file.c_str() );
    std::getline( infile , Name );
}

void name::run() { show_name(); }

Thank you in advance. :-)

Share this post


Link to post
Share on other sites
Advertisement

You a little off base on your understanding, but that's OK, considering your a beginner.  Depending on your experience level, you might not be quite ready for a Component-based Entity system.  But, I'll just try and cover the basics.

 

An entity is really just something that groups multiple components together, and, in game programming, these entities are typically things like can be represented in a game.  It could be represented by a unique integer id (so, every time a new entity is created, the id the entity gets is just incremented), but how the components are tied to the entity can take multiple forms.  For example, it could do this:

 

struct Entity
{
  int Id;
  std::vector<TComponent*> Components;
};

In this case, the entity actually holds the components it is made of.  But, you can also do it this way

typedef int TEntityId;
 
struct TComponent
{
  TComponent(TEntityId entityOwner) : EntityOwner(entityOwner) {}
  TEntityId EntityOwner;
};
 
struct TPosition : TComponent
{
  TPosition(float x, float y, TEntityId entityOwner) :
    TComponent(entityOwner), X(x), Y(y)
  float X;
  float Y;
};

 

In this case, the entity isn't really defined, just it's type, and each component created knows which entity owns it.

 

You typically wouldn't have components like "message" or "name".  A typical Entity, for example, an enemy spaceship, might be made up of these compoennts:

TPhysicalObject : This holds the spaceship's physical properties: size, location, velocity, mass, etc.

TEnemy : This component denotes this object as an enemy, and might have properties defining how the enemy moves and acts (does it dive at the player, or just shoot his bullets at a rapid rate?)

TWeapon : This component might define what kind of weapon the enemy has, and defines the type of bullet entities that the ship will create when it's fired.

THealth : This would just define what the health is of the enemy ship.

TAnimation : This would hold the enemy's animation data, which spires to display when, etc. 

 

Using just those components, you would create "systems" that act on these components, so, you would have a MovementSystem, that acts on all entities that have the TPhysicalObject component, and a EnemySystem that acts on all entities that have the TEnemy Component.

 

Realize, the gain you will get from using this system will really come from having many different kinds of entities, and you want to tweak them using an external data (json objects defining entities or components, etc.).  I'm not sure I would advise you trying this at an early stage in your programming career, but if you do, good luck and keep at it.

 

BTW, I have some articles in my journal (in my sig) covering some ECS details if you want to look over it.

Share this post


Link to post
Share on other sites

Thank you very much for your answer. It's a good starting point for me to start learning the more complicated aspects of programming. In the next days I will look at your journal posts and experiment. :-)

 

One question, though: I want to get an explanation on what exactly is going on in these parts:

TComponent(TEntityId entityOwner) : EntityOwner(entityOwner) {}

TPosition(float x, float y, TEntityId entityOwner) : TComponent(entityOwner), X(x), Y(y) {}

What exactly should I google? The reference on structures does not cover this part. :-/

 

Based on your second example, I made this little test which I will experiment with in the next days:

typedef int entity;

struct component
{
    component( entity id ) : ID( id ) {}
    entity ID;
};

struct message : component
{
    std::string Word;
    message( std::string word , entity id ) : component( id ) , Word( word ) { std::cout << Word << std::endl; }
};

int main( int argc , char * argv[] )
{
    entity e = 123;
    entity e2 = 456;
    message( "Hello! :-D I'm entity 1!" , e );
    message( "...And I'm entity 2! Goodbye! :-)" , e2 );

    std::cin.ignore();
    return 0;
}

I know there won't be actual components like "message", it's just my way of testing the implementation with a "Hello world" type of test program. :-)

Share this post


Link to post
Share on other sites

One question, though: I want to get an explanation on what exactly is going on in these parts:
TComponent(TEntityId entityOwner) : EntityOwner(entityOwner) {}

TPosition(float x, float y, TEntityId entityOwner) : TComponent(entityOwner), X(x), Y(y) {}
What exactly should I google? The reference on structures does not cover this part. :-/

 

In this case, I'm using struct just as I would use a class.  The TPosition struct inherits from the TComponent struct.  In the initializer list, I call the parent class constructor (TComponent(entityOwner) in this case) with the entity owner id, so it will set the entity owner, and then I set the other values in TPosition.

 

Also, you should expound your example.  In your message struct, don't do the std::cout in the constructor.  Rather, create a MessageSystem function call, which would take the entities that have a message component, and do the cout in the MessageSystem.

 

Also, when assigning Entity Id's, just use a global that is incremented every time you create a entity.  that way it is guaranteed to be unique.

 

Take a look at this ECS framework, rather than creating your own: entityx

Edited by BeerNutts

Share this post


Link to post
Share on other sites

One more question, about your first example:

struct Entity
{
  int Id;
  std::vector<TComponent*> Components;
};

Doesn't that vector cause problems with inheritance? If I try to run a function from a component that inherits from the base component class/struct, won't it only run the base component's function instead of the inheriting component's?

Share this post


Link to post
Share on other sites

One more question, about your first example:

struct Entity
{
  int Id;
  std::vector<TComponent*> Components;
};

Doesn't that vector cause problems with inheritance? If I try to run a function from a component that inherits from the base component class/struct, won't it only run the base component's function instead of the inheriting component's?

The reasons for virtual functions in C++ is just that: Although you have a pointer to an object of the base class, the object may in fact be of any class inheriting that base class, and invoking a virtual function already declared in the base class then in fact invoke an implementation overridden by the derived class. A typical candidate would be Component::update(). BUT ...

 

... one possible concept of ECS, and that concept is favored by BeerNutts, is to make components as data holders only. Any usage (i.e. a function working on that data) are concentrated in sub-systems (see again BeerNutts first post and look for "MovementSystem" and "EnemySystem", for example). Another concept would be to allow for both data components and behavior components, but still making a distinction.

 

Why is this useful? Look at a component that represents the placement of the entity in the world. It may be manipulated by a controller or animation first, then read by the collision sub-system, perhaps a collision resolution is needed that again alters the component's value. Later is is read by the graphic rendering to determine the world matrix. Such a data component can best be understood as (perhaps complex) variable: It has a type (and can/should additionally have a semantic meaning), but how it is used is outside of the scope of the variable itself.

Edited by haegarr

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!