11
\$\begingroup\$

In designing an entity-component system for my engine, I've come across a little snag in the way of storing and retrieving a particular type of component.

First, let me set clear up a bit of terminology I'm going to use in this question:

  • I call "Component" a data structure that stores relevant data for a particular system.
  • I call "System" an aggregation of methods and data structures that makes use of Components to update game state/interface with the user.
  • An "Entity" is basically just an ID used to retrieve specific components and modify their data in game logic.

Each system owns an (ID-mapped) array of its type of Component (E.g Physics->PhysicsComponent, AI->AIComponent, Rendering->RenderingComponent), so that it can iterate efficiently over data.

Not all components are specifically owned by a system, however. For example, a Transform component stores an object's position, rotation and scale. It's one of the most important parts of an entity (Unity makes it mandatory, even), since it is used by a lot of systems, e.g. Physics, AI, Rendering etc.

This is pretty much the problem I'm facing. Since Transform is used by a lot of other systems, how should I go about retrieving one to use for each Component? One possible solution I see is to make each Component store its own Entity ID. It would be easy to retrieve any component like this, but it wouldn't be that efficient, and it also would go against the concept of a Component as an isolated and independent bundle of data, that isn't aware of any other.

Is there a proper way to solve this problem? Should Transform even be a component?

\$\endgroup\$
  • 3
    \$\begingroup\$ +1 for "First, let me set clear up a bit of terminology I'm going to use in this question:" \$\endgroup\$ – Vaillancourt Apr 9 '17 at 1:03
  • \$\begingroup\$ I would love to see this type of questions more in this site. +1 \$\endgroup\$ – S. Tarık Çetin Apr 10 '17 at 12:50
  • \$\begingroup\$ Just store all components as global variables \$\endgroup\$ – Miles Rout Apr 11 '17 at 19:53
2
\$\begingroup\$

This is quite a broad question, the answer to which, strongly depends on your architecture. However, I will attempt to give you a general answer.

Your physics and rendering systems will certainly require the transform, however, the AI system will not. Therefore, it makes sense to encapsulate the transform into it's own component class. All such interested systems would use the same data, therefore, it makes sense for the entity to have a pointer to the transform object, or an id for a transform object stored elsewhere.

If you choose the latter solution, then each system that is interested in a transform will require access to wherever the transform object is stored.

If you choose the former, then all each system needs to do is access the entity itself, and request the transform.

In the former case, the problem becomes how to grant access to the storage for transforms to each system, without breaking OOP rules, if you care about such things.

The latter case has no such issues, but requires changing your entity object design to store pointers to objects rather than ids of component objects.

My personal preference is to design the entity class to store pointers to component objects, as it simplifies many design issues. In this way, each system which requires a transform can request it from the entity, and ignore it if it doesn't. However, this does carry a computational overhead inherent to pointers, that being the cost of cache misses.

Have a look at this ECS Overview for more information on this.

At the end of the day, It's up to you to decide which is more important to you: ease of development, or performance.

I would point out, finally, that your question is an excellent example of the design questions that ECS proponents think about, and there is no definitive silver bullet solution.

\$\endgroup\$
  • \$\begingroup\$ Thanks for your suggestions. I have a question, though: why wouldn't the AI system need an object's position? \$\endgroup\$ – CRefice Apr 9 '17 at 15:20
  • \$\begingroup\$ You are confusing position ( vector of 3 floats) with a Translation (transform matrix constructed from a position vector). A Transformation matrix is constructed from translation, rotation, and scale transforms. This is far more information than an AI system would need, though you certainly could extract the position vector from it. Personally though, I would separate position, orientation, and sizes out into their own component, and use them to create and update the Transform. \$\endgroup\$ – Ian Young Apr 9 '17 at 17:04
  • \$\begingroup\$ @IanYoung Separating them could do more harm than good, potentially if you find you need position and orientation together more often than you do position or orientation separately. In that case, placing position and orientation data attributes in a single component can improve cache performance. \$\endgroup\$ – Naros Apr 9 '17 at 20:59
  • 1
    \$\begingroup\$ My preference is to combine all three, position, orientation, and scale into a single component and in cases where a specific subsystsem needs just a position or orientation, I'd advocate duplicating the data and synchronizing them at clearly defined points in the game loop. \$\endgroup\$ – Naros Apr 9 '17 at 21:01
  • \$\begingroup\$ @Naros yes this is what I meant: Two Components, Transform, and (in my framework) SpatialData, which contains position, velocity, orientation, and angular velocity. The position and orientation are used to construct and update the Transform. \$\endgroup\$ – Ian Young Apr 9 '17 at 22:16

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.