How Not To Do An Entity System Entities The Oo Way With Logi

Experiences in implementing an Entity System the OO way

Some background about this approach: I've read the article from t-machine and got interested in the concept and looked around for source code. The first source code I've found was the nebula3 engine and I've tried that approach in a lite version (Nebula3 uses entities with logic and a messaging system included). Long story short, it was a big disaster. Here is my small development history and what I got.

I wanted to write a simple 3d engine with objects and the bullet physic engine. For that I had the following components:

  • rendering: containing everything needed for the 3d-engine
  • transform: contains a transformation matrix with position, rotation and scaling
  • physic: containing everything needed for bullet

Now the problems started, the physic-component also has a transform matrix which is needed by bullet and needs to be in sync with the transform-component. To make it easier for the user the transform component has a function to set the transform matrix and this function looks if the entity also has a physic component and updates the transform matrix of the physic, too:

void CompTransform::setTransformMatrix(Eigen::Matrix4f &newTransform, bool updatePhysicComponent /*= true*/) {
 mTransform = newTransform;
   /* HACK, but otherwise I have to include an event-system with notifications :/ */
   CompRender *component = (CompRender*)getOwnerContainer()->getComponent("CompRender");
   if(component != NULL)
      component->setTransformMatrix(newTransform);

   if(updatePhysicComponent) {
      CompPhysic *component = (CompPhysic*)getOwnerContainer()->getComponent("CompPhysic");
      if(component != NULL) {
         btTransform bulletTransform;
         bulletTransform.setFromOpenGLMatrix(newTransform.data());
         component->setWorldTransform(bulletTransform);
      }
   }
}

Now if the user sets the transformation matrix the physic if updated automatically. But if bullet calculates the new position of the object, it needs to update the transformation component, so in the physic component the following code can be found:

void CompPhysic::setWorldTransform(const btTransform& centerOfMassWorldTrans) {
   //Called from Physic-Engine if the position changes, so update the renderer
   CompTransform *component = (CompTransform *)getOwnerContainer()->getComponent("CompTransform");
   if(component != NULL) {
      Eigen::Matrix4f transform;
      centerOfMassWorldTrans.getOpenGLMatrix(transform.data());
      component->setTransformMatrix(transform, false);      //do NOT update the physic-component again
   }
}

Because the transformation component doesn't know who called setTransformMatrix a flag is needed in the transformation component if the physics matrix should be updated too, otherwise it would create an endless loop. For this I've included a flag "updatePhysicComponent".

The design of the components got really ugly and unmaintainable really fast!, this was where I stopped developing this solution further because it had too many dependencies and stuff needed to keep in mind during development and I realized that with more components it would get much nastier.

In an approach where an entities never has any logic the update-step of bullet and setting the transformations from the transform component to the physic component would be much cleaner and more separated!

Here some pseudocode:

listTransform = EntitySystem::getEntitiesWithTransformComponent();
updatePhysicTransform(listTransform);
listPhysic = EntitySystem::getEntitiesWithPhysicComponent();
updatePhysic(listPhysic);
copyPhysicTransformToNormalTransform(listPhysic);

Of course you need a way to make sure, if there is a physic component there also has to be a transform component, but it's not checked on every single entity.