6
\$\begingroup\$

Why are entity component systems the way they are? For example as far as I have seen it may look like this

class Entity
    list of components
    add component
    remove component
    update comentent
    ....

So you are building your entities at runtime. I assume it will look like this

 Entity ork;
 ork.add(AI).add(movement).add(physics).add(renderer)

The problem I see with this approach is that errors appear at run time instead of compile time. It also seems to add a lot of potential errors for example the AI system only makes sense if the entity also has a movement component.

What is the advantage of building your objects that way instead of doing it like this?

class Ork: IEntity
     movementComp
     AiComp
     physicsComp
     rendererComp
     ....
     //wire everything together

One possible advantage that I can see is that object creation takes less code because you don't really need to wire up your objects, you just add your components and your done.

Would you please enlighten me?

\$\endgroup\$
6
\$\begingroup\$

Partly to allow data-driven operations, and partly because an ECS is a specific subset of all possible component-based designs.

A main point of component-based approach to aggregation (be it ECS or another use of components) is to allow you to reconfigure game objects without needing a recompile. You have an editor that your designers and game scripters can use to build up game objects without needing to bug a programmer and then wait for a new build (often requiring hours or even a full day to get rolled out to the content team).

There are perfectly valid component architectures that use static (compile-time) aggregation for components. This kind of class is just fine and is used in shipping games:

class Monster {
  TransformComponent m_Transform;
  MeshComponent m_Mesh;
  AnimationComponent m_Animation;

public:
  // accessors and forwarders

  // long repetetive methods like the following are common in this approach
  void handleMessage(const Message& msg) {
    m_Transform.handleMessage(msg);
    m_Mesh.handleMessage(msg);
    m_Animation.handleMessage(msg);
};

It's not an ECS but it is still a valid component architecture.

With an ECS specifically each component is intended to be external to the core game object. This is because one of the motivations for ECS is data-oriented design (not the same data-driven design!).

Data-oriented techniques focus on how the CPU uses data and this in turn shows that it's better to process data in small chunks that are allocated contiguously in memory. Putting a component object directly into a game object is antithetical to data-oriented design.

Grokking data-oriented design is essential to performance but it's very debatable whether your components are the right place to apply it. ECS assumes that your components are the main data being processed which may or may not be true for your particular engine/game. Performance-sensitive data in many engines tends to be external to the game objects/components.

\$\endgroup\$
3
\$\begingroup\$

If you know what an entity would require to wrap it up in an object, ECS doesn't stop you from doing the same in an iterative way. In a system, if an entity has the required components, the target components will be updated. If an AI component requires other components, it will only be updated if the entities has them. If it does not, a different behaviour can be chosen (typically, just moves on to the next entity).

This can be used to do interesting things. If your entity has an AI component that required a movement component, and the entity does not have it, it can choose to add a different AI, and return to the previous behaviour when such Movement component was added.

An example could be a freeze spell, that just removes the Movement component, and adds it back after it finishes it's effect. A frozen AI unit could switch it's behaviour to AIRanged (using shield, spells, try to remove the freeze), based on an AI state machine.

I am not too experienced with ECS, but I believe this could explain a bit.

\$\endgroup\$
  • \$\begingroup\$ Actually a freeze spell most likely intercepts the input actions to the entity and merely causes specific movement actions not to be fired, thus giving you the illusion that the movement component doesn't exist. Adding/Removing components come at a certain amount of cost, particularly if they're components which have dependencies in the hierarchy. Its usually better to intercept & block things temporary (aka disable) rather than completely remove & readd (just an FYI). \$\endgroup\$ – Naros Jan 16 '14 at 17:29
  • \$\begingroup\$ In my current ECS implementation, the freeze occurs at the PerformActionSystem, which handles doing actions such as walk. Altough the freeze does prevent here for actions to continue to be done regarding movement, it wont stop current movement from happening, so a character could continuously slide down if other forces other than input actions were applied. It's why I suggested removing the movement component. But I am the first to admit, I am not very experienced with ECS, so I don't know what is the optimal approach to many things. \$\endgroup\$ – rvalerio Jan 16 '14 at 18:00
  • \$\begingroup\$ Rather than removing the movement component, the movement component should be aware of the freeze effect (with, for example, a simple boolean flag), and stop motion if it is in effect. (The spell component would be responsible for setting and clearing this flag.) Dynamically adding and removing components after an entity is created means that things such as caching pointers to other components of the entities aren't safe. \$\endgroup\$ – user41442 Feb 6 '14 at 21:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.