2
\$\begingroup\$

[I understand that this this could be seen as a rather theoretical question, but I think it has real-application impact].

"Academic" base
With a component-based design one wants to get rid of the long inheritance chains known in larger games ("deadly diamond of death"). So we create a BaseComponent class that handles all the administrative chores (ID and type handling, possibly a generic list of properties, ...).

Now each actual Componentshould inherit from that base [start of academic/dogmatic flair] and only that base [end].

Concrete situation/example:
Assume I have three components with different attributes:

  1. Person: age, name, gender, isTired, isSick, ...
  2. Guest: satisfaction, desires, ...
  3. Employee: current duty, shift end, ...

Now in traditional OOP design, Person would act as the base class for the other two. As I currently "forbid" that type of inheritance for the components in my project, everytime I am e.g. processing my employees and checking/updating their state attributes I have to either use some helper function to access the sibling component Person (if it exists) or send a message out that hopefully Person will be registered for and will process. Or I can simply assume that each Guest and Employee always will have the component Person. But then I might as well let those two components inherit from Person, so they for example both have the member functions to manipulate some specific properties.

Note: There will never be an Entity that has the component Employee, but not component Person (same applies to Guest). They are a fix requirement, which is why I am considering inheritance here at all.

Question: My question is now whether anyone can see any real-life problems that would be introduced by (re-)incorporating a bit of inheritance into my components other than to a degree violating the original base/dogmatic concept and the risk that (if used excessively) I would end up with the exact same problem the component-based design tried to solve in the first place?

References:
Thread that ultimately led my to asking that question, showing such inheritance.

\$\endgroup\$
5
  • \$\begingroup\$ Depends on what kind of inheritance will you create. If you make component called Person and then component called Guest that inehirts from it then it could later be a problem (because such concrete components as "age" never worked for me). Games were written even without components, and with inheritance. I've used inheritance in components and it worked (and it was easier to write it that way, since i didnt need to register so many component types with just update method) \$\endgroup\$
    – Kikaimaru
    Commented Jan 6, 2013 at 11:48
  • \$\begingroup\$ @Kikaimaru "age" is not a component in my scenario, but merely a property the component Personhas \$\endgroup\$ Commented Jan 6, 2013 at 12:10
  • \$\begingroup\$ "deadly diamond of death"? No need to exaggerate, especially while understating the possibilities. Composition effectively counteracts this problem. \$\endgroup\$
    – snake5
    Commented Jan 6, 2013 at 12:41
  • \$\begingroup\$ @snake5 Well, to my knowledge that is the colloquial term for that issue so I think it fits in here. I am aware that composition is an approach to solve that, hence my question in that area :) . \$\endgroup\$ Commented Jan 6, 2013 at 12:54
  • \$\begingroup\$ Oh, it's deadly enough once you find out you were representing a structure as an inheritance hierarchy when you really should have been using composition. Because it can cost a lot of time. \$\endgroup\$
    – Engineer
    Commented Jan 6, 2013 at 12:57

3 Answers 3

2
\$\begingroup\$

Question: My question is now whether anyone can see any real-life problems that would be introduced by (re-)incorporating a bit of inheritance into my components other than to a degree violating the original base/dogmatic concept and the risk that (if used excessively) I would end up with the exact same problem the component-based design tried to solve in the first place?

By doing that, you make explicit the requirement that every guest is a person, and that every employee is a person, but you've also added the implicit requirement that no guest is an employee (or vice versa) because then they would share 2 copies of 'person' data. You will need to think about how you manage that. And, once you have put in place a system that allows you to forbid certain pairs of components on the same entity, you will see that an almost identical system would allow you to enforce the presence of one component on an entity if another is present.

So, given that you have to address the mutual component compatibility problem one way or another, I'd be inclined to suggest you're better off keeping Person and Guest/Employee separate. Not only is further separation a good thing in software development terms (eg. the Single Responsibility Principle) but it leaves your design open to future extensions to Robot Employees, Animal Guests, or Guest Employees.

(PS. There is nothing inherently wrong with inheritance as a programming language construct. Component based systems for games are not about trying to remove the use of inheritance from the code base but about finding a more flexible way to represent the large permutation of traits that can apply to any single entity in the game. You might have inheritance in hundreds of places throughout your code but the components mean you never have to derive anything from the Entity class that exists in your game world.)

\$\endgroup\$
2
\$\begingroup\$

You are concerned about the diamond problem in regards to some minor entity inheritance. Don't be. Inheritance is for is-a relationships. Composition (as used to construct entities) is for has-a relationships. Inheritance hierarchies for components are nearly always is-a.

Mick West's seminal article on the topic deserves a thorough read-through, as it gets down to the heart of the issue of how processing should work. You will note in the final part of his article that OO is not strictly a requirement, as you can do this with nothing but procedural programming and monolithic arrays.

Keep your inheritance at the component level, there is nothing wrong with a bit of inheritance here. There should OTOH be only one level for Entity, no subclassing unless there is something very specific you are trying to achieve... one example that comes to mind is having a lighter-weight entity class such as EntityLite that saves on memory consumption by holding fewer component references, and might be used for say, particles that don't need things like AI, inventory and so on. As for inheritance in components, I've found myself using it e.g. for the idea of health and mana tracking components being similar, so I subclassed these and then filled in the differences in the subclasses representing each component. It is a valid pattern.

The aspect which you noted in the comments to this response is a separate matter. Certainly you can try to access other entities from a global list or hash, but really the reference to the entity you are trying to access should always be gotten from some more direct mechanism, often geometrically / physically based, or faction-based. So you could get a particular team member to send them a message based off the fact that you have a list of team member entities (example might be AI inspecting it's teammates, then throwing some ammo to whichever soldier has the least ammo) or based off the fact that you did eg. a raycast collsion with that character (example might be an AI talking to someone it can see directly) which returns you the collided object and thus the entity you are looking at. It's important to get those communication channels right from the start, rather than selecting some arbitrary entity out of a massive list, if only for efficiency reasons.

\$\endgroup\$
12
  • \$\begingroup\$ I was aware of that article, as I read pretty much everything I could find on that topic so far :) . So basically you are saying that I should stick with my current approach as described in my question (which basically is what Mike preaches), that if I need to get/set data in another component than the one processed right now, I have to check whether this sibling exists and if it does, then do the change. \$\endgroup\$ Commented Jan 6, 2013 at 12:08
  • \$\begingroup\$ @PhilipAllgaier Are you sure you want be accessing other entities' components directly during an update? In my experience that leads to potential synchronisation issues. If anything it is safer to update a global model of some sort, and apply changes to other entities at the end of the tick, or on the next tick, by inspecting those cached deltas. \$\endgroup\$
    – Engineer
    Commented Jan 6, 2013 at 12:13
  • \$\begingroup\$ @PhilipAllgaier I am editing my answer, as I feel we are talking at odds. \$\endgroup\$
    – Engineer
    Commented Jan 6, 2013 at 12:18
  • \$\begingroup\$ So you are proposing that I create some sort of DeltaManager that during an update run caches all requested property changes that a component A (that is currently processed) wants to make to component B. Once all components have been updated, go through the DeltaManager and actually carry out the changes? If so, I see the issue that perhaps a subsequent change made by component C to component B might still be queued in although it doesn't make sens after the first update anymore. So my DeltaManager would also need to be able to handle conditions -> lots of extra complexity. \$\endgroup\$ Commented Jan 6, 2013 at 12:28
  • \$\begingroup\$ @PhilipAllgaier Indeed. But I wanted to warn you about synchronisation issues. Question has now been edited. Let me know if that addresses your concerns. \$\endgroup\$
    – Engineer
    Commented Jan 6, 2013 at 12:29
1
\$\begingroup\$

I'm not sure about your Person / Guest / Employee example (depends on what you want to do with these components) but I have used deeper inheritance for my graphical components:

abstract GraphicalComponent : Component {...}
//has abstract GetAABB and Draw methods
//there is a system to manage all GraphicalComponents

SpriteComponent : GraphicalComponent {...}
//draws textured quad

ParticleEmitter : GraphicalComponent {...}
//draws many textured quads

SkeletonComponents : GraphicalComponent {...}
//for skeletal animation

I don't see any benefits from having separate system for each type of graphical object. With one system I can implement spatial partitioning, layers, etc.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .