Component Based Entity System Question

Started by
11 comments, last by SiS-Shadowman 12 years, 7 months ago
[font="Arial, sans-serif"]Hey all, I would like your opinion on something...Okay so:

Say you have a component based entity system and your system is kind of normal:

Where an Entity is a ComponentContainer and an AttributeContainer.

Components are things that use attributes (The entity holds the attributes so that two components can say...access the Mesh attribute or health...) and can add\get attributes to an Entity.

Attributes are pretty obvious.

Now lets say you have your Renderable3D component, and when it gets added to an Entity or created you need it to also get added to the Renderer so it can be rendered...

How would you do this? Make the Renderer a singleton? (OnEntityAdded or Initialise on the component could then just add itself...)

[/font][font="Arial, sans-serif"]Or have a GameSystem that would hold a Renderer* and was a singleton?[/font]
[font="Arial, sans-serif"] [/font]
[font="Arial, sans-serif"]Or...? A solution without singletons!?[/font]

Engineering Manager at Deloitte Australia

Advertisement
Why not construct the Renderer with a list of Renderables, inject the entity container into the Renderer so that it can request a list of renderables, subscribe the renderer to the entity system so that it listens for when a Renderable3D is attached, or any of a million other scenarios that don't involve singletons?
My system is pretty similar to yours, but instead of the entities holding the attributes, components do. When another component requires the same attribute, it must say so when it's attached to the entity (via requirements). Anyways, I have a big entity system that consists of subsystems. Each subsystem handles ONE specific type of a component. The one system holds all renderables, the other one all collidables, and so forth. This way I can render all renderables "at once" (the subsystem obviously holds an entity => component dictionary) and do collision tests for all components in the other subsystem.

So basically you add one system per component (sometimes I do need a small hierarchy in components, so I've restricted my rule to having one subsystem per DIRECT subclass of component: all further subclasses are put into the same system). If you're interested, I can send you my sources.


[font="Arial, sans-serif"]Hey all, I would like your opinion on something...Okay so:

Say you have a component based entity system and your system is kind of normal:

Where an Entity is a ComponentContainer and an AttributeContainer.

Components are things that use attributes (The entity holds the attributes so that two components can say...access the Mesh attribute or health...) and can add\get attributes to an Entity.

Attributes are pretty obvious.

Now lets say you have your Renderable3D component, and when it gets added to an Entity or created you need it to also get added to the Renderer so it can be rendered...

How would you do this? Make the Renderer a singleton? (OnEntityAdded or Initialise on the component could then just add itself...)

[/font][font="Arial, sans-serif"]Or have a GameSystem that would hold a Renderer* and was a singleton?[/font]
[font="Arial, sans-serif"] [/font]
[font="Arial, sans-serif"]Or...? A solution without singletons!?[/font]


I cheat. I have a method call on my entity class, which it delegates to all its components: void populateRenderList(MainRenderList list)

This way, on each frame, anything within the frustum is asked to add anything it wishes to the render list. This is highly useful for components which aren't normally drawn, but which I might want to draw something for debugging purposes.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
[font="Arial, sans-serif"]Now lets say you have your Renderable3D component, and when it gets added to an Entity or created you need it to also get added to the Renderer so it can be rendered...
How would you do this? Make the Renderer a singleton? (OnEntityAdded or Initialise on the component could then just add itself...)
[/font][font="Arial, sans-serif"]Or have a GameSystem that would hold a Renderer* and was a singleton?[/font]
[font="Arial, sans-serif"]Or...? A solution without singletons!?[/font]
Singletons are almost never the right solution, especially in high-level code like this.

The easiest way (IMO) to enforce this restriction ("when it is created you need it to also get added to the Renderer") is to require the renderer be passed to the constructor. In order to create a Renderable3D component, you need to have a reference to the Renderer (which you should be able to pass around without resorting to global variables...)
class Renderable3D
{
public:
Renderable3D( Renderer& r ) : parent(r) { parent.Add( this ); }
~Renderable3D() { parent.Remove( this ); }

static void AddToEntity( Entity& e, Renderer& r )
{
e.AddComponent( new Renderable3D(r) );
}
private:
Renderer& parent;
Renderable3D();
Renderable3D( const Renderable3D& );
};


However, I'd prefer a simpler design that only has components and systems -- no attributes or even entities -- the game code can create it's own entities however it likes by grouping together component handles however it likes (probably with entities being programmed in another language like Lua, rather than in C++)class RendererSystem
{
public:
int CreateRenderable()
{//some simple handle-based allocation, e.g.
int id = components.size();
components.resize( id + 1 );
return id;
}
private:
std::vector<Renderable3D> components;
};

struct MyRenderableEntity
{
MyRenderableEntity( RendererSystem& r ) : myRenderable(r.CreateRenderable()) {}
int myRenderable;
}

[font="Arial, sans-serif"]How would you do this? Make the Renderer a singleton? (OnEntityAdded or Initialise on the component could then just add itself...)

[/font][font="Arial, sans-serif"]Or have a GameSystem that would hold a Renderer* and was a singleton?[/font]
[font="Arial, sans-serif"] [/font]
[font="Arial, sans-serif"]Or...? A solution without singletons!?[/font]


I've just been putting a first-attempt at an entity system into my wip over the last couple of days. It's probably got issues, and I've combined my attributes and component concept into one - but I've gone for a solution that allows me to do something like this:

[source lang="cpp"]
//Render all the ModelC components
ModelC::RenderAll(); //ModelC maintains all instances of its type and this function calls Render() for_each

//Add a ModelC to entity via one of several methods
entity->AddComponent(CID_MODEL);
//or
ModelC *model = ModelC::Create(entity); //the object is added to ModelC; we can safely let the newComponent pointer fall out of scope

//Access a ModelC for entity
ModelC *model = ModelC::Get(entity);
[/source]

The implementation relies on each component class simply having a static std::vector containing pointers to all instances of its type. So... yeah it requires static data, but not singletons, per se.

Why not construct the Renderer with a list of Renderables, inject the entity container into the Renderer so that it can request a list of renderables, subscribe the renderer to the entity system so that it listens for when a Renderable3D is attached, or any of a million other scenarios that don't involve singletons?


Those are all great ideas, however I still don't see how the user of my system will easily create an Entity that is able to be Rendered. Easy as in, oh I just give it a 3DRenderable component with a file name to a mesh and it is now being rendered at it's position! (Preferably they will also just be defining this entity in an XML file, or even further in an editor). I am leaning towards your third proposed idea, however that way the entity system will need to know about the Renderer so it can add a 3DRenderable if an entity is added with one attached to it...Yes?

If I was going to force them to get a Renderer pointer and give it to the 3DRenderable, how would I let them get that pointer?

Wow those are a lot of responses with a lot of good discussion material and they are making me re-think my current system! I will have to read and respond to them all later as it's 12:36 AM here in Brisbane and I have a meeting at 9:35 AM!

Thanks all! biggrin.gif

Engineering Manager at Deloitte Australia

Each of my components have a component loader (simple factory), my entity factory has all these "loader" objects. The graphical one does whatever it need to (loading models, attaching things and what not) - it knows how to add things to the scene and so on. The component doesn't need to know these things (at some point it might). I can define my entities using xml like:

<Entity>
<Components>
<Graphical>
<SceneNode>
<Mesh>
</Mesh>
</SceneNode>
</Graphics>
</Components>
</Entity>


The entity factory checks if it can load a given component type, if it can - then it goes ahead uses its sub factory to do so. As I'm using factories, once the entity factory is initially created, I do longer need to pass around anything like the scene/physics and so the factory can be used pretty much anywhere without problem.

My Graphical component just holds a "SceneNode" pointer, thats enough to do what I need with it (move the visuals mainly).

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

On 9/14/2011 at 5:37 AM, CRYP7IK said:

lets say you have your Renderable3D component, and when it gets added to an Entity or created you need it to also get added to the Renderer so it can be rendered...

How would you do this?

The rendering system should own the renderables, and the entities should get references to them them from the rendering system.

This takes care of the registering-renderables-with-the-renderer issue. But it also allows the rendering system to manage the memory of the data it works with, so that it can process in a somewhat cache coherent way.

On 9/14/2011 at 7:37 AM, CRYP7IK said:

...the entity system will need to know about the Renderer so it can add a 3DRenderable if an entity is added with one attached to it...Yes?

Yeah, the components may handle all interaction with their respective systems, but at some level whatever code decides it needs one of those components to add to an entity will need access to those systems to either get the components or register them. Depending on how things work, this code may not actually live in the entity system.


My system is pretty similar to yours, but instead of the entities holding the attributes, components do. When another component requires the same attribute, it must say so when it's attached to the entity (via requirements). Anyways, I have a big entity system that consists of subsystems. Each subsystem handles ONE specific type of a component. The one system holds all renderables, the other one all collidables, and so forth. This way I can render all renderables "at once" (the subsystem obviously holds an entity => component dictionary) and do collision tests for all components in the other subsystem.

So basically you add one system per component (sometimes I do need a small hierarchy in components, so I've restricted my rule to having one subsystem per DIRECT subclass of component: all further subclasses are put into the same system). If you're interested, I can send you my sources.


It is not completely obvious to me why a sub-system holds a dictionary of entity -> component (I assume by component, it is the actual component the sub-system deals with) that may be because:

A. My components can keep references to attributes that the entity has. (If you need position from the entity in the dictionary)
B. My components have a reference to the entity that contains it.

Do your components not know about their entities?


I cheat. I have a method call on my entity class, which it delegates to all its components: void populateRenderList(MainRenderList list)

This way, on each frame, anything within the frustum is asked to add anything it wishes to the render list. This is highly useful for components which aren't normally drawn, but which I might want to draw something for debugging purposes.


So the list gets given to the Renderer...how?

For example, lets say I was coding away in my GameState and I wanted to create an entity that is just a box that can be knocked around with physics:
(This is my current way, that I want to change from using singletons.)


Entity* myEntity = new Entity();
myEntity->AddComponent(new Renderable3D("box.mesh", true, Position));
myEntity->AddComponent(new PhysicsObject3D(meshBoundsShape, Weight));



Now in those three constructors they each add themselves to each system, the Entity gets an ID from the GameSystem (Via singleton) and puts itself into the GameSystem the same way. The Renderable3D does the same with it's RenderSystem and same with the PhysicsObject3D...This makes it easy for a user of my system to create entities because things auto-magically go into their systems.

[font="arial, verdana, tahoma, sans-serif"]

Singletons are almost never the right solution, especially in high-level code like this.



Yeah, that's what I am trying to fix. My thinking that singletons fix everything![/font]


The easiest way (IMO) to enforce this restriction ("when it is created you need it to also get added to the Renderer") is to require the renderer be passed to the constructor. In order to create a Renderable3D component, you need to have a reference to the Renderer (which you should be able to pass around without resorting to global variables...)


That's great and all, but then how does the user of this system access the renderer, do I have to force them to create it themselves in whatever state they want to render in?

Using your first example, contrasting to my above one:


Entity myEntity;
AddRenderable3DToEntity(myEntity, rendererThatIDontHaveHowDoIGetIt);



would you then have to go:


Renderer* renderer = new Renderer(stuff);

Entity myEntity;
AddRenderable3DToEntity(myEntity, *renderer );
ChangeState("game", New GameState(), renderer); // Change state and pass in Renderer...


I feel that is bad, because my user then has to worry about keeping his renderer available, which he may then just might make a global variable anyway...


However, I'd prefer a simpler design that only has components and systems -- no attributes or even entities -- the game code can create it's own entities however it likes by grouping together component handles however it likes (probably with entities being programmed in another language like Lua, rather than in C++)
class RendererSystem
{
public:
int CreateRenderable()
{//some simple handle-based allocation, e.g.
int id = components.size();
components.resize( id + 1 );
return id;
}
private:
std::vector<Renderable3D> components;
};

struct MyRenderableEntity
{
MyRenderableEntity( RendererSystem& r ) : myRenderable(r.CreateRenderable()) {}
int myRenderable;
}



I kind of like this idea, but then how does the RenderSystem get to the game code where you are creating that entity? This is how I originally started doing this kind of system, however I didn't know how to make it data-driven and easily make it able to be scripted.

Man I am so confused now...

Engineering Manager at Deloitte Australia

This topic is closed to new replies.

Advertisement