Unseen Academy

Why?

Here is a good description why and how such a system works: Inner Product about component systems. I've found an article in the gameprogramming gems, but it was without a working example, so I've used the listings from the book to build one.


ComponentSystem.zip

Version 2

On the blog from T=Machine he presented an implementation from him for android. I've taken his java-code and rewrote it for c++, now I think it's much clearer what a component system (or entity system) is and what not. I think it's really important that an entity has NO logic included and the system is only for bookkeeping of the entities. If logic is added to an entity it quickly get really nasty!


EntitySystem_V2.cpp

The V2 has a serious bug in finding entities, so DO NOT USE THIS VERSION!

Version 3

After I've made a little test-application to test component systems in combination with multicore algorithms I've found that I can simplify the entity system by removing the entity-ids. Now the entity system only works with pointers to entities and components.


To test the simplified version I've written a small (brute force) nbody simulation where the work of updating the components is split onto multiple cores by the nulstein task scheduler. And it works really well!

At first the entites are created and initialized with the needed components: for(uint_t i=0; i<numberBodies; ++i) { CompPosition3D *compPos3D = new CompPosition3D(); compPos3D->x = lcrgf.randf1to1(); compPos3D->y = lcrgf.randf1to1(); compPos3D->z = lcrgf.randf1to1(); CompPosition3DTemporary *compPos3DTemp = new CompPosition3DTemporary(); CompVelocity *compVelocity = new CompVelocity(); CompPosition2D *compPos2D = new CompPosition2D(); entitySystem.addComponent(&entities[i], compPos3D); entitySystem.addComponent(&entities[i], compPos3DTemp); entitySystem.addComponent(&entities[i], compVelocity); entitySystem.addComponent(&entities[i], compPos2D); } And in the update-loop for every entity with a position component the acting forces are calculated to move the particle, the new position is written to the normal position and the projection is done, every time in parallel loops spread over all cores of the processor: NBodyIntegrationStep integrationStep(entitiesWithPosition, (float)0.001); bOk = ParallelFor(&integrationStep, ParallelRange(0, entitiesWithPosition.size(), 20)); NBodyCopyTempPos copyTempPos(entitiesWithPosition); bOk = ParallelFor(©TempPos, ParallelRange(0, entitiesWithPosition.size(), 20)); Project3Dto2D project3Dto2D(entitiesWithPosition); bOk = ParallelFor(&project3Dto2D, ParallelRange(0, entitiesWithPosition.size(), 20));


EntitySystem_V3.cpp

Version 4

Okay, the next version isn't a big change in the sources, but a big change in execution speed and a paradigm shift on entities. On the T-Machine-Blog the author had written, that in an entity there is NEVER ANY data. But to get the component system faster I have placed the pointers of the components into the entities: struct Entity { static EntitySystem *entitySystem; Entity(); template<typename Type> Type *getAs(); std::map<FamilyId, Component*> mComponents; }; I have done this do get the following access-pattern optimized: CompPosition3D *bodyPos3D = mEntities[i]->getAs<CompPosition3D>(); CompPosition2D *bodyPos2D = mEntities[i]->getAs<CompPosition2D>(); In the V3 of the system (with no data in an entity) EVERY access to a component searched for the entity in the componentstore. std::map<FamilyId, std::map<Entity*, Component*> > mComponentStore; template<typename T> T *getComponent(Entity *e) { std::map<Entity*, Component*> &store = mComponentStore[T::familyId]; return (T*)store[e]; } Now this is much faster because I already know the entity-pointer and have only to search for the correct familyId(and the data I have to search is much less). And the entity system keeps a collection of entities sorted by familyId.

To compare both versions I have coded a simple benchmark where I create 2000 entities, each with 4 components and I have measured how long it took to touch in every entity all 4 components, 10000 times. In the V3 it took 3.8s on my machine (6.12s on my laptop), the V4 took only 0.92s(1.35s on the laptop)!

Here is the complete example with the program compiled in release-mode for your pleasure:


NBodyRelease.zip