Jump to content
  • Advertisement
Sign in to follow this  
landagen

Entity-Component Design Questions

This topic is 1931 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 have recently read the Entity-Component article on here and I got really interested in the design.  It sounds great.  Functionality through property definitions.  I have been waiting for the followup article about implementation, but it has not come yet.  I have been reading and thinking about my own implementation and have a few questions.  First, I want to start off with my assumptions/design.

 

1.  All components inherit from a base component class that contains some pure virtual functions to be overwritten.

2.  Components contain only data and do not directly have functionality.

3.  All entities will be kept in a single container as the master list of entities.

4.  Entities are a class that can contain one or more components.

5.  A system can query the master list looking for entities that contain all required components.  Once it finds one, it will then typically reference that entity by an ID number, not a pointer

 

 

Some of my design/assumptions may already be drifting away from the concept.  I am trying to stay close to the concept because I feel that if I drift away from the component, then I may end up running into the snags the design was supposed to prevent.  So without further ado, here are my questions:

 

1.  How to provide custom functionality for entities that contain the exact same components.  For instance, I may want to have different AI routines for different entities.  My thought was to have a component that is a functor to the particular AI routine I would want to run.  That would be a required component in order for the AI system to find that entity


2.  How do most people deal with changes to the component list?  Do they use the observer pattern to inform systems when an entity changes or does the system query the master list every time to find the entities that match its criteria.  The observer pattern is the way I was thinking.


3.  How do you handle shared components.  An example would be several entities using the same material.  My thought was to just have a component that contains a shared_ptr to the material. 

 

4.  Is it okay to have some inheritance at the component level or would this break the design?  Based on what I know, I can't imagine it would break the design.  I wanted for instance my graphics system to be able to find all entities that contain a VertexBuffer component, but I also want my material system to be looking for a particular type of VertexBuffer component (say one that provides texels).   

 

5.  My understanding is that most systems contain a reference to an entity by entity ID and not by pointer.  Why is that?  Is it common for the pointer to change, but the ID to remain the same?  If so, what scenarios would that occur in?  Does constant lookup not cause performance issues? 


Also, any articles that go into some specifics about an Entity-Component system would be very helpful.  Thanks. 

Share this post


Link to post
Share on other sites
Advertisement

Thanks Juliean, this helps a lot.  Could you give some examples of components so that I can have a better understanding of the scope of a component.  I understood it to be like a position component, or a mass component, etc.  Basically you would group data in your components in a way that fits your systems.  For instance, position would be in a component by itself because it is used by the render system and the physics system, but other types of data only make sense together such as material data so it may be all contained in one component.  Another question I have is what is an ideal amount of components on average in an entity system.  I can see the trade-off of flexibility versus performance, but where is the sweet spot?  

Edited by landagen

Share this post


Link to post
Share on other sites

It depends a lot on your needs. I can show you some of my components:

 

struct Position : Component<Position>
{
	Position(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Direction : Component<Direction>
{
	Direction(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Rotation : Component<Rotation>
{
	Rotation(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Scalation : Component<Scalation>
{
	Scalation(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Bounding : Component<Bounding>
{
	Bounding(const BoundingBox& box) : m_box(box) {};

	BoundingBox m_box;
};

/* Light components */



struct Ambient : Component<Ambient>
{
    Ambient(const FColor& ambient): m_ambient(ambient) {};

    FColor m_ambient;
};

struct Diffuse : Component<Diffuse>
{
    Diffuse(const FColor& diffuse): m_diffuse(diffuse) {};

    FColor m_diffuse;
};

struct Specular : Component<Specular>
{
    Specular(const FColor& specular): m_specular(specular) {};

    FColor m_specular;
};

 

Note that it is probably personal choice if you want to have position, scalation etc.. seperated or stored inside a single "Transformation" component holding a matrix. Also note that you could argue over whether it makes sense to have seperate components for light, etc... . As long as you do not overload your components with unnecessary data and/or function, and do not overload the system with low-level functionality, everything is fine.

 

Another question I have is what is an ideal amount of components on average in an entity system. I can see the trade-off of flexibility versus performance, but where is the sweet spot?

 

Well in a good implementation, the amout of components should have little to no impact on performance, at least for processing, creation is another topic, but even that should come with only a very suddle cost. It all depends on the programming languege of your choice, though. In c++, you can use bitfields and static functions to quickly search for all entities with specifiy components, and access those components just as fast. variadic templates, e.g in c++11 make it even more easy.

Share this post


Link to post
Share on other sites
1. Split the components even more. For AI I have a pathfinding component, a chase component, an attack component and so on. I don't have a problem with very specific components for certain tasks either, such as a sliding door component. You may be surprised at how easily a complex system can be broken up and how much simpler the design becomes when you do that.

2. Very low level systems such as physics and rendering that need to know about changes immediately should be managing their own data directly (and the components only carry an ID). For the rest I just iterate through the entity list.

3. Yes, share the data outside the component system and make the components distinct.

4. I would not use inheritance within components at all, except perhaps to derive from an abstract base component. The example of a vertex buffer is a poor one also as rendering APIs do not use inheritance in that way.

5. It doesn't matter much whether you use IDs or pointers. For me, pointers are slightly more accessible because you dont need to go back to the system to get at the data. I would use smart pointers at least to avoid the risk of memory leaks or dangling pointers.

Share this post


Link to post
Share on other sites

One good article I read about entity-component-based-systems was this one about Ash in AS3:

http://www.richardlord.net/blog/what-is-an-entity-framework

 

It depends a lot on your needs. I can show you some of my components:

 

struct Position : Component<Position>
{
	Position(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Direction : Component<Direction>
{
	Direction(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Rotation : Component<Rotation>
{
	Rotation(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Scalation : Component<Scalation>
{
	Scalation(float x, float y, float z): m_x(x), m_y(y), m_z(z) {};

	float m_x, m_y, m_z;
};

struct Bounding : Component<Bounding>
{
	Bounding(const BoundingBox& box) : m_box(box) {};

	BoundingBox m_box;
};

/* Light components */



struct Ambient : Component<Ambient>
{
    Ambient(const FColor& ambient): m_ambient(ambient) {};

    FColor m_ambient;
};

struct Diffuse : Component<Diffuse>
{
    Diffuse(const FColor& diffuse): m_diffuse(diffuse) {};

    FColor m_diffuse;
};

struct Specular : Component<Specular>
{
    Specular(const FColor& specular): m_specular(specular) {};

    FColor m_specular;
};

 

I wonder,what are you using the templateparameters for? I'm still trying to find a good design, too.

Edited by BloodyEpi

Share this post


Link to post
Share on other sites

I wonder,what are you using the templateparameters for?

 

Ah, a speciality of my implementation - thats how I map my components to a bitfield. In short, the template is used determine the exact type of the component via a few static variables.

 

#pragma once

/*********************************
* Base component class
*********************************/

struct BaseComponent
{
	typedef unsigned int Family;

protected:
	static Family family_count;
};

/*********************************
* Component class
**********************************/

template <typename Derived> //template to pick implementation
struct Component : public BaseComponent {
	static Family family(void);
};

template<typename C>
unsigned int Component<C>::family(void) {
	//increase base family count on first access
	static BaseComponent::Family Family = family_count++; 
	return Family;
}

Now whenever I call "ComponentName::family()", it is completely assured that this meets the actual components "family" (or type, you could say), without special precautions.

Edited by Juliean

Share this post


Link to post
Share on other sites

thanks! +1

 

I was going with strings set by the programmer, but this is a lot better and avoids type collision.

Share this post


Link to post
Share on other sites

2. Very low level systems such as physics and rendering that need to know about changes immediately should be managing their own data directly (and the components only carry an ID). For the rest I just iterate through the entity list.
 

 

Could you elaborate on this?  Are you saying that the ID in the component would essentially point to the data that is owned by another system (aka the physics system or the rendering system) and so for anyone to request data specific to one of those systems they would query that system by the ID in the component? 

Share this post


Link to post
Share on other sites

Could you elaborate on this? Are you saying that the ID in the component would essentially point to the data that is owned by another system (aka the physics system or the rendering system) and so for anyone to request data specific to one of those systems they would query that system by the ID in the component?

 

What he is saying is probably like what I suggested, that physics and rendering should be as little related to the entity system as possible. So in the best case, you wouldn't need to query the phyiscs system in any of your entity systems at all - just one time to sync physic bodys transform with the entity. A pointer or an ID would do the job, depending on the modularity you want for your system, and whether you want to do "easy swapping" - changing physics body reference without any logic applied to it, by simply chaning an nurmic id, that is - or you find it more convienient to fetch a pointer when chaning the id externally. More a matter of choice again, as I think. Same applies to every other low-level functionality you want to use with your components.

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!