C++ Entity/Component System Setup Assistance
Hey taurusrow
I have been experimenting with component based object system now for a couple of years and the results have been getting better and better each time, currently I am actually streaming my current programming project live on http://www.twitch.tv/devmoon
I am using a very decoupled component/subsystem approach where each subsystem is completely independent and currently I have these systems but it will be more later
- Transform
- Physics
- Collision
- Timer
- Editable
- Render
To answer your questions
An object consists of the following
- Unique ID
- Object name (a pointer to a template that keep tracks of the name it was instanced with)
- Array or component pointers
- Internal messaging system (being able to communicate with the components)
A component consists of
- Pointer to the object its attached to (so it can communicate)
- Custom data its working on (position in the transform component etc..)
- Pointer to the subsystem
A subsystem consists of
- A vector of components (that is pre allocated)
- Updates the components that have been used by any object
From my experience component based object systems is not some kind of "solve all your problems" but just another way to manage your game objects. You still need to craft it to fit your needs.
To update everything I have an ordered list of all my subsystems and call their update method
Here is also a script example of how it can be used (lua script)
sample_object =
{
on_created = function(self)
self:add_component("Transform")
self:add_component("Physics")
self:add_component("Collision", {width = 10, height = 10})
self:add_component("Render")
self:add_component("Editable")
self:add_component("Timer", {[70] = instance_destroy})
end,
on_collision_enter = function(self, other)
if (other.solid) then
instance_destroy(self)
end
end,
-- if there is no render method there is no overhead of trying to call one
on_render = function(self, x, y, z)
-- some custom draw code here if needed
end
}
I do stream programming almost every day and you can ask me anything and I might make all the code available for public at a later date
Good luck with the component systems it is really fun to work with
There are many possible approaches to an Entity/Component-system. Kristoffer Lindström already made a good starting point, but I'd like to present you a slightly modified method, which further decouples components and systems:
In this method, your components wouldn't have a pointer to a sub-system (which I'm going to referer to as "systems" from now on). Your systems wouldn't have a list of components, too. Instead, you would store all systems in a list, and in a generic "update" method you would implement the functionality. Systems would then act on Entities based on what components they have - a render system might pull out all entities with translation and model, a physics system will search for entities with translation and a bounding volume.
Main advantage here is that messaging between systems, entities and components is neglectible. You wouldn't need any messaging at all for the basic functionality. Additionally, this approach is even more decoupled - components are POD structures, that don't know anything about systems, or even that a system exists. Components also don't know about the entitiy that holds them.
While both approaches are plausible, I prefer mine (*duh*), because for the given reasons. But in the end its probably a matter of personal choice, while I still think it would be easier to implement an approach similar to mine, since you don't need a complex messaging system, which might be a hard thing to start with.
Yea that is the charm of the component/entity system, there is so many variations of it. My version integrates nicely with my lua implementation and allows me to do what I need, in the end the requirements you have on your engine/game will decide how sometime as important as the object management will be done.
You can also do really easy and slimmed component implementations, and actually I am using something similar to your technique Juliean when creating particle systems.
@Kristoffer: Thanks for the reply and further explanation. I will look for your streaming to see if I can pick anything up
@Juliean: I appreciate your input as well
I'm going to keep trying to work at it, or find some sort of step by step tutorial to get the first parts set up and functional. The Interfaces to create the components and systems, using the correct smart pointers for references, etc. I really like the idea of the design, and Kristoffer, I agree - it seems like it is really fun to work with. Especially once you get into it and can create editors and such which just tag on components to entities.
I've done a fair bit of ECS programming myself. If you look at my journal (link in my Sig), you can find some posts where I got through my 1st version of ECS, and then when I modified it, and created a 2nd version.
The first version had component containing both Logic and Data. It communicated with other components via an event/messaging system. It worked OK for what i was doing, but I wasn't real thrilled with how the messaging was working.
So, I created another ECS system (called the Escape system, found here) which mimics more of what Juliean describes. There is no messaging, only systems operate on the components data. The systems can operate on a single Component, or on an entity that contains multiple specific components; the system, upon initialization sets what type of component an entity must have forit's Update to be called with that entity.
Here is some code where i setup some systems, and I create some components ad link them to an entity:
pWorld = Esc::TWorld::GetInstance();
PhysicsSystem = new TPhysicsSystem(mpSpace);
RenderingSystem = new TRenderingSystem(app);
DamageSystem = new TDamageSystem();
DeathSystem = new TDeathSystem(mpSpace);
InputSystem = new TInputSystem(app);
EnemySystem = new TEnemySystem();
pWorld->AddSystem(InputSystem);
pWorld->AddSystem(EnemySystem, 1000, true);
pWorld->AddSystem(PhysicsSystem);
pWorld->AddSystem(RenderingSystem);
pWorld->AddSystem(DamageSystem);
pWorld->AddSystem(DeathSystem);
Esc::TEntityPtr Entity1;
Entity1 = pWorld->CreateEntity();
TGraphicsObject* graphicComp(new TGraphicsObject(Utilities::ImageGet("gfx/player.bmp")));
TPhysicalObject* physicalComp;
THealth* healthComp(new THealth(100, 100));
TInput* inputComp(new TInput());
// Create a Physics object
physicalComp = new TPhysicalObject(mpSpace, pBody, pShape, 200.0f);
pWorld->AddComponent(Entity1, graphicComp);
pWorld->AddComponent(Entity1, physicalComp);
pWorld->AddComponent(Entity1, healthComp);
pWorld->AddComponent(Entity1, inputComp);
pWorld->SetGroup(Entity1, "Player");
pWorld->AddEntity(Entity1);
Here is code for a rendering system. Look at the Initialize() function, it defines it only wants to deal with entities that have a Physical Object Component, and a graphical Object component.
TRenderingSystem::TRenderingSystem(sf::RenderWindow *pApp) :
Esc::TSystem(), mpApp(pApp)
{
}
TRenderingSystem::~TRenderingSystem()
{
}
void TRenderingSystem::Update(Esc::TEntityPtr entity, uint32_t tickDelta)
{
TPhysicalObject *physicalObject =
static_cast<TPhysicalObject*>(entity->GetComponent("PhysicalObject"));
TGraphicsObject *graphicObject =
static_cast<TGraphicsObject*>(entity->GetComponent("GraphicsObject"));
graphicObject->GetSprite()->SetPosition((int)physicalObject->GetBody()->p.x,
(int)physicalObject->GetBody()->p.y);
graphicObject->GetSprite()->SetRotation(physicalObject->GetBody()->a*(180.0f/PI));
mpApp->Draw(*(graphicObject->GetSprite()));
}
void TRenderingSystem::Initialize()
{
// Set which components we want to deal with
Esc::TSystem::HandleComponent("PhysicalObject", true);
Esc::TSystem::HandleComponent("GraphicsObject", true);
}
Here is code where I setup the graphics component, just to give you an idea:
class TGraphicsObject : public Esc::TComponent
{
public:
TGraphicsObject(sf::Image *image) : Esc::TComponent("GraphicsObject"), SfmlImage(image)
{
if (image) {
SfmlSprite = new sf::Sprite();
SfmlSprite->SetImage(*SfmlImage);
SfmlSprite->SetOrigin(SfmlImage->GetWidth()/2, SfmlImage->GetHeight()/2);
SfmlSprite->SetRotation(0*(180.0f/PI));
}
}
~TGraphicsObject() { delete SfmlSprite; }
bool IsImage() { return (SfmlImage != NULL); }
sf::Sprite *GetSprite() { return SfmlSprite; }
void GetSize(uint32_t &width, uint32_t &height)
{
width = SfmlImage->GetWidth();
height = SfmlImage->GetHeight();
}
private:
sf::Image *SfmlImage;
sf::Sprite *SfmlSprite;
};
There is a portable very good C++ implementation of an Entity-Component system, available at https://github.com/alecthomas/entityx. If you want to do an implementation of your own, please have a look at the readme of this system first.
I think this implementation fulfills most requirements on easy-of-use, decoupling and efficiency (including what Juliean stated above). It also has an built-in support for callbacks, to be used for low frequency events.
On messaging systems, I think you could do away with it entirely in lieu of using components to transfer the message.
For example, you are testing for collisions. Instead of throwing a message to something that, hey, a collision occurred, you would simply add a collided component to the entities in question, the data of which would be the ID of the entity that it collided with. Then any systems that cared about collisions would look through the list of collided components and grab all of the necessary data from the other components it cares about and do what it does.
I'm surprised no one linked to Understanding Component-Entity-Systems.
I am surprised by the emphasis on messaging. The system I am using has
- no implicit messaging: I couldn't figure out a proper behavior that would make sense in general
- no parent-child relationships, admittedly for the lack of time in implementing those
- no need to poll for components: I operate on them directly instead
A pointer to a template... of the name... it was instanced with. So it's variable name or not?Object name (a pointer to a template that keep tracks of the name it was instanced with)
I'm surprised no one linked to Understanding Component-Entity-Systems.
I am surprised by the emphasis on messaging. The system I am using has
- no implicit messaging: I couldn't figure out a proper behavior that would make sense in general
- no parent-child relationships, admittedly for the lack of time in implementing those
- no need to poll for components: I operate on them directly instead
A pointer to a template... of the name... it was instanced with. So it's variable name or not?Object name (a pointer to a template that keep tracks of the name it was instanced with)
Yea that was kind of badly worded, what I meant is that each object in the scene was created using something like
local inst = instance_create("ObjectName")
-- where this could be an object
ObjectName =
{
on_created = function(self)
self:add_component("Transform")
end
}
So the object holds a pointer to an ObjectTemplate that has the string name and a connection to the Lua table.
My messaging system operates by components binding a callback to a message name on the object, whenever a component posts something the object calls all the other components that subscribed to that particular message. This is mostly used to hook stuff when components gets added or removed.