Component based design - what is state of the art?

Started by
14 comments, last by NightCreature83 11 years, 8 months ago
I'm on the record for hating component based programming in current programming languages, but alternatives aren't production ready and I have a problem where it seems well suited:

I have a real life thing that has a number of capabilities/behaviors/features that may or may not be there. Those capabilities are static for each thing, but there are dozens of combinations of capabilities. Currently there are hundreds of capabilities. They're different enough that they can't be nicely abstracted. The biggest problem is that we add a few new capabilities, and 3-5 new combinations every few months; and need those deployed independently enough that it's not a giant mess.


So that's the background. It certainly seems like a good candidate for making the various capabilities into independent components.
We're using C#, but examples in other languages are fine. Mostly I'm wondering what the current state of the art is with component based programming?

Most of the threads here focus on idealistic design, and ignore the practical aspects of component detection, dependency resolution, inter-component communication, and probably a dozen other things I'm not even aware of. Where's info about actual implementations?
Advertisement
It's AI focused, but has good general applicability anyways - check out the GDC Vault lecture "Creating Your Building Blocks: Modular Component AI Systems" (2011) for some really solid advice.

Slides (free) and video (requires login to the vault).

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Michael A. Carr-Robb-John has had a whole bunch of post on Component based design on altdevblogaday which you can find here, you can find links to the beginning of the series at the bottom of the page.

I read an article a while back about components in Prototype I think it was which was very enlightening in how to make components fast. This is their GDC presentation but I have seen another one in which they go into a bit more detail but lost the link sadly.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion


check out the GDC Vault lecture "Creating Your Building Blocks: Modular Component AI Systems" (2011) for some really solid advice.


I know that C++'s RTTI blows, but making your own? Bleh.

[edit:]

I wish there were more info about the 'resolve' step discussed here. And I dislike that the components seem to know about the entity that owns them.
In my humble personal opinion: "Component based systems" / "Component/entity systems", which come in the form of a complex framework based around RTTI/reflection/virtual/instanceOf/etc, are just typical over-engineered enterprise bullshit.

You don't need some complex system/framework to program in this style. Just program in this style. Component-based design was around long before people realised that it was a better approach to game entities than deep-inheritance-trees... and it's the same people who never really groked OOP and as a result gave us the deep-inheritance-tree entity framework, who are now giving us over-engineered component frameworks... Hell if they just used OOP correctly to begin with, they'd end up with something similar to the ideal component-based design anyway, but no time to reflect on our flaws, there's a bandwagon ahead! </rant>

In my humble personal opinion: "Component based systems" / "Component/entity systems", which come in the form of a complex framework based around RTTI/reflection/virtual/instanceOf/etc, are just typical over-engineered enterprise bullshit.


I largely agree, though I also look at what can be done using more conventional OO designs and am increasingly unconvinced that they can solve the problem at hand sufficiently.
Writing in a component-oriented style is just good engineering. I'm with Hodgman though that frameworks and all that jazz are typically a waste.

I find that when it comes to the style, it's like any other engineering advice: highly situational and context-sensitive. There are so many different-but-similar problems that can be effectively solved with the component style that blanket, generalized advice is almost useless. IMHO the reason that people tend to be hesitant to prescribe solutions (the framework-bandwagon camp notwithstanding) is that they understand this fact.

It really boils down to having to think on your feet a bit and take the general ideas and use them to create an effective design for whatever problem you're trying to solve.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

That's the thing though; to me, the general ideas are not good ideas.

You shouldn't just have a bundle of unknown components that you can't interact with reliably and certainly can't get to interact with each other reliably.

But I've seen enough articles that hint that people are doing it, and having enough success that they espouse the benefits of such a design that I must be at least partly mistaken. So I'm trying to find out where I am mistaken. Maybe there are tricks I have not seen to make things less of an issue in practice. Maybe there are benefits I can't see or are underestimating. Maybe I'm just overestimating the prevalence of the designs or what constitutes success to these people.

But everything public I see is either vague, or not really being used. I just want to know where I'm mistaken in my views, and I'd rather not have to prototype out something significant to learn those lessons.
IMHO the (you can already call it) classical component-based architecture with entities as the central scene object containing various components is a rather arbitrarily chosen model. Sure, it is in many if not most cases far superioir to the inheritance based approach which must have exploded in popularity (I guess) around the time C++ became a viable alternative to C. (?)

But what exactly does an entity represent? An entity without components is nothing but an ID. The real information and behaviour lies within the components and their interactions with each other so basically an entity is just a 'thing' in this model. And 'things', in software design, are generally considered bad.
And what is a component in this model? A component is something that goes into an entity, Duh!
I think this model was chosen because it represents better the visual thinking of designers and maps well to graphical editors.

I'm on the same track as Apoch and Hodgman here. Writing in a sensible OOP style should lead you naturally to something you could call a component system.
However, we should think more in terms of largely independent subsystems with well defined touching points than to focus on the individual objects.
Obviously that means some kind of message-passing/data-transfer mechanism which I think could benefit from a robust framework.

Well, some random thoughts there hehe. I'm actually in the process of trying some things out myself. I'd love to read more discussion on this subject, so keep 'em coming,
There are many definitions of what a component is. As mentioned there are countless ways to implement such a design.

So to answer you question about communication... when is communication actually needed? Well depending on your design, communication mights happen between entities, or even between components. It really depends of your design's requirements and expected behavior. This is the main key, you need to set yourself requirements and abide by them. Usually with such communication, these messages (or events) will become extremely complicated and even sometimes redundant. As your project grows, you will need to introduce a bunch of new messages that will trigger all kinds of logic. This will lead to spaghetti code and very hard to debug applications.

For my design, my requirements were like so:
- A component is only data
- An entity is an array of components
- A system does logic on entities with specific component(s)

With these requirements, anything that would need inter-entity communications could be done without. Since components and entities have no logic, they cannot communicate. Systems can do whatever they want though, so communication between systems is possible, though I try to use components as communication messages. It is still possible for systems to have a reference to other systems and invoke them to do something depending on what happens. It really depends on the situation.

With entity component systems, programming logic became a bit different than what I was used to. There are a wide possibilities of making things happen, and flexibility is highly increased.


Let's take this scenario:
In our game, when a player kills another, he gets +1 score.

How would I program this with my requirements?

Well, a player would obviously be an entity.
A player can "target" and "damage" an entity and has "health", as well as a "score".
A system will need to process damages and another scoring.

The trick here is that one piece of logic leads to another... if damage kills other(s), score increases. We could have the damage system hold a reference to the scoring system, and if health of an entity reaches 0, then invoke the scoring system. That would be perfectly fine, but I think that would restrict flexibility later on. I really like to keep system logic modular and well separated so that I can reuse them in other projects. To do so, I introduce a new component, "killHistory", that tells us the amounts of kills the entity had since the last time the scoring system processed that entity. This new component will act as a trigger for the scoringSystem to do some logic.


So let's review what we have so far:
Components:
- "damage": the amount of damage that the entity can cause
- "target": the entities that are attacked and that will receive damage
- "health": entity's health level
- "score": entity's score
- "killHistory": amounts of kills since the last time the scoringSys processed the entity

Entities & [components]:
- "player1": ["damage", "target", "health", "score", "killHistory"]
- "player2": ["damage", "target", "health", "score", "killHistory"]
- "player3": ["damage", "target", "health", "score", "killHistory"]

Systems:
- "damageSys": deals with "damage", "target", "health" components
- "scoringSys": deals with "killHistory" component

I'll assume that all systems are ran once a frame. This of course isn't optimal, especially if you have a lot of entities to process at every frame. It would be up to you to make things efficient (for example: having a dirty flag in components).

Now say player1 targets player2 & player3. This attack kills both player2 & player3.
Here is what would happen.

"damageSys" is invoked for the current frame. When going through player1 entity, it sees that it's targetting player2&3. It grabs those entities from the target component and decreases player2&3's health by player1's damage value. player2&3 are now dead, so it increases player1's "killHistory" by 2. "damageSys" has done its job. It doesn't care about anything else.

"scoringSys" is invoked for the current frame. It seems when processing player1 that its "killHistory" value is != 0. It adds "killHistory" to player1's score. It then resets the "killHistory" component to 0. Boom! It is now done with its job.


That was a very simple example, but hopefuly it shows the wide range of possibilities depending on your design. It is also how I usually think of emplementing new features using components as raw data, and systems as logic using specific components.

I have my c++ component entity system up on bitbucket, and if anyone is interested, I could write up a very simple example of how to use.

I hope it helped a bit.

This topic is closed to new replies.

Advertisement