Initially I considered using an std::map to relate entities to their component lists, but I really liked that they used a vector in Artemis to store all the entities. An O(1) lookup time is much better than O(log n) when you're dealing with several lookups per entity per frame. Like in Artemis, I opted to use a separate vector with removed entity indices. Whenever an entity is added, the system checks that vector first to see if there is a hole in the main vector. This reduces fragmentation and covers the case where the user removes several entities before adding one.
Unlike Artemis, I used an event system to decouple the entity manager and entity systems. The entity manager fires an event whenever a component is added, allowing the entity systems to check that the entity still has all of the required components. I figured that my game will use an event manager, so it made sense to integrate one now. Also, other subsystems could be notified whenever an entity is created/destroyed or a component is inserted/removed from a specific entity. The entity systems themselves contain a std::set< Entity* > data structure that holds all of the active entities. When it receives one of the previously mentioned events, it can check the entity to see if it should be added to or removed from the set.
Here's what I have so far in my main.cpp test program:
// Create the event manager and entity manager
evtMgr = new SGF::EventManager();
entMgr = new SGF::EntityManager( evtMgr );
test = new SGF::TestSystem(evtMgr, entMgr);
// Create an entity
SGF::Entity *e = entMgr->CreateEntity();
SGF::Transform *trans = new SGF::Transform();
entMgr->InsertComponent(e, trans);
while( !wnd.HasQuit() )
{
wnd.PumpMessages();
ID3D11DeviceContext *dc = graphics->GetImmDeviceContext();
IDXGISwapChain *sc = wnd.GetSwapChain();
{
wnd.SetAsRenderTarget(dc);
dc->ClearDepthStencilView(wnd.GetDSView(), 0, 1.0f, 0);
dc->ClearRenderTargetView(wnd.GetRTView(), D3DXCOLOR(0.0f,0.0f,1.0f,1.0f));
sc->Present(1, 0);
}
// This loops through any entities that match the bit flags for the component type. In this case, all it wants is a Transform component.
test->Process();
}
entMgr->DestroyEntity(e);
delete test;
delete entMgr;
delete evtMgr;
Here's the source for my TestSystem class:
// TestSystem.h /////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef __TEST_SYSTEM_H__
#define __TEST_SYSTEM_H__
#include "EntityProcessingSystem.h"
namespace SGF
{
class TestSystem : public EntityProcessingSystem {
public:
TestSystem( EventManager *eventManager, EntityManager *entityManager );
virtual ~TestSystem();
protected:
static unsigned int TypeBits;
virtual void ProcessEntity( EntityManager *manager, Entity *e );
};
};
#endif
// TestSystem.cpp /////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
#include "TestSystem.h"
#include "EntityManager.h"
#include "ComponentMapper.h"
#include "Transform.h"
#include
using namespace SGF;
unsigned int TestSystem::TypeBits = (1 << CT_TRANSFORM);
TestSystem::TestSystem( EventManager *eventManager, EntityManager *entityManager )
: EntityProcessingSystem(eventManager, entityManager, TypeBits)
{}
TestSystem::~TestSystem() {}
void TestSystem::ProcessEntity( EntityManager *manager, Entity *e )
{
// Allows you to map the list as a specific component type.
ComponentMapper tmap = manager->GetComponentList(e, CT_TRANSFORM);
// We can index a specific component of a specific type.
assert( 0 && tmap[0]->mat._11 );
}
If you look closely at the last line, you'll notice that the component system supports multiple components per type. This is another difference from the Artemis framework, which only allows one per type. The reason I chose to diverge from their design in that respect is because I can think of at least a few key cases where it would be really nice to have multiple components per type. For instance, if I have an articulate vehicle entity, I might have the chassis connected to the wheels. With this system, I could have four joint components--each one connected to a wheel.
At any rate. If anyone is interested in the code, I'd be happy to provide it. It's essentially just a port of Artemis with a couple nuanced changes and additions.
EDIT: I got sidetracked and I haven't made much progress since this post. I decided I would just post what I have: http://cse.taylor.edu/~zbethel/ArtemisPortCpp.zip
If you have any questions, feel free to ask.