Sign in to follow this  

Component based design - what is state of the art?

This topic is 1964 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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 [i]hundreds[/i] 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? Edited by Telastyn

Share this post


Link to post
Share on other sites
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.

[url=http://www.gdcvault.com/play/1014573/Creating-Your-Building-Blocks-Modular]Slides[/url] (free) and [url=http://www.gdcvault.com/play/1014572/Creating-Your-Building-Blocks-Modular]video[/url] (requires login to the vault). Edited by ApochPiQ
Clarification.

Share this post


Link to post
Share on other sites
Michael A. Carr-Robb-John has had a whole bunch of post on Component based design on altdevblogaday which you can find [url="http://www.altdevblogaday.com/2011/09/23/the-game-entity-part-v-future-ponderings/"]here[/url], 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 [url="http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CGwQFjAA&url=http%3A%2F%2Fwww.gdcvault.com%2Fplay%2F1911%2F&ei=BTcPUNKfMO210QXBtoCYBg&usg=AFQjCNGb-99w9QEcfnWX7dtT-Izd7FD50w&sig2=-tYvrL2EBcfzC1WLoD8zeA"]GDC[/url] presentation but I have seen another one in which they go into a bit more detail but lost the link sadly.

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1343168352' post='4962757']
check out the GDC Vault lecture "Creating Your Building Blocks: Modular Component AI Systems" (2011) for some really solid advice.
[/quote]

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. Edited by Telastyn

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1343225059' post='4962935']
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.
[/quote]

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.

Share this post


Link to post
Share on other sites
Writing in a component-oriented [i]style[/i] is just good engineering. I'm with Hodgman though that [i]frameworks[/i] and all that jazz are typically a waste.

I find that when it comes to the [i]style[/i], 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.

Share this post


Link to post
Share on other sites
That's the thing though; to me, the general ideas are [b]not[/b] 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 [i]each other[/i] 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.

Share this post


Link to post
Share on other sites
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,

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
[quote name='french_hustler' timestamp='1343253285' post='4963075']
I hope it helped a bit.
[/quote]

Not really. I know the theory behind the thing, but you're going right over all of the things that matter.

How are the components wired together, or your entities and the systems? How do they [i]know[/i] where to go and how to interact with things. When you add killHistory, how is that hooked into the other entities so that it gets updated? What happens when things can die from not-damage?

Edit all of the different things that can cause something else to die? No. The entire point of these structures is to make it that components can be added without going back and screwing around with existing ones.

You don't spell out how these cascading effects actually align so they don't cause cycles or thrashing or work at all since they're order dependent. You don't spell out how components [i]see[/i] state changes. Keeping the last known state is... no. Events just end up in some cascading ball of poo.


Sorry to be curt, but this is the sort of thing I see [i]all the time[/i] with component systems. (Everyone) Quit giving me [b]it depends[/b] hand waving or a super high level view. I know that your implementation is for your requirements, and I know that the terms can cover a wide range of possible designs.

What did you implement? For what requirements? How did you (in gory technical detail) solve the problems of:[list=1]
[*]Determining what components were available to the entity via configuration, including tying all of the dependencies together.
[*]Communicating between components within a common entity.
[*]Exposing the aggregate functionality so that the entity was more than 'update this every frame or so' (unless you found that unnecessary)
[/list]
And I'm fine without component based entities for those that think that's horrible. Here's what I need to do:[list=1]
[*]Read some data that defines a device.
[*]Using that data, populate the features that the device can support. There are ~300 features, and for the sake of discussion, they share no natural traits. They all have various behaviors that need to be set and otherwise twiddled, and tend to interact with one another. A given device will almost always have >100 features. About 8 of them are on every device.
[*]Allow new configs, and new features that can be deployed independently of existing ones.
[/list]
Right now, the existing code base is by and large a giant (both wide and deep) inheritance hierarchy, populated by string dictionaries and wired together with tin cans and string (generic ValueChanged events). This blows goats.

If a conventional OO design can do these things well, that would be awesome. But I don't see it. I also don't see how a component system can be implemented without getting into a giant grey gooey mess that is impossible to debug and fragile as hell. Edited by Telastyn

Share this post


Link to post
Share on other sites
Since I haven't seen it mentioned thus far, I'll just go ahead and point out the [url="http://en.wikipedia.org/wiki/Decorator_pattern"]Decorator Design Pattern[/url]. The UML and description at the top is not the greatest, but the examples underneath provide a clearer picture of the pattern.

Share this post


Link to post
Share on other sites
[quote]
Not really. I know the theory behind the thing, but you're going right over all of the things that matter.

How are the components wired together, or your entities and the systems? How do they [i]know[/i] where to go and how to interact with things.
[/quote]
If a component is purely data, why would they be wired together? In which entities they go really depends on you. A component is just data that make up an entity. It represents the characteristics of an entity.

[quote]
When you add killHistory, how is that hooked into the other entities so that it gets updated? What happens when things can die from not-damage?
[/quote]
The killHistory component was just a way to avoid direct communication between 2 different systems. In the example I've mentioned, the scoringSystem works on entities that have both a killHistory and score components. You could have a system directly communicate with another if you wanted to. If something can die from some other causes than damage, then you'd have an other system do logic for that. If a player can be poisoned, create a poisoningSystem which diminishes the health component if an entity also has a drugged component for example. It really is up to you. The mechanism is there for you to program using a data driven paradigm.

[quote]
Edit all of the different things that can cause something else to die? No. The entire point of these structures is to make it that components can be added without going back and screwing around with existing ones.
[/quote]
Yes, components can be added, but using the requirements I have mentioned earlier, a system must also be in place to do logic based on these components. If you create component "foo" and there is no system that processes it, then there is no point. Systems apply logic depending on specific components they work with.

[quote]
You don't spell out how these cascading effects actually align so they don't cause cycles or thrashing or work at all since they're order dependent. You don't spell out how components [i]see[/i] state changes. Keeping the last known state is... no. Events just end up in some cascading ball of poo.
[/quote]
I have specific classes as the core of my engine that help out with these things. When I create a new system, I apply component requirements. This system will only process entities with these requirements. I then add this system in my system manager which works with my entity manager. The entity manager keeps track of "families" for each system. A family is a list of entities that have the proper components required by each registered system in the system manager. I also have an event manager that can run systems periodically, right away, or even in a certain order. For example, my rendering system is triggered by my event manager at a pace of 60 fps. As far as components seeing state changes... they don't. Again components have no logic what so ever. Systems see if entities have updated changes. I have yet to implement that as part of my core, but there are multiple ways to approach that problem depending on your engine architecture.

[quote]
Sorry to be curt
[/quote]
No problem, I'm just trying to help...

[quote]
Determining what components were available to the entity via configuration, including tying all of the dependencies together.
[/quote]
I don't think I understand this. Only dependencies I have are component requirements for each system. I solved this using families (tracked in my entity manager).

[quote]
Communicating between components within a common entity.
[/quote]
Again, communication can only happen where there is logic. Only systems can interacts with each other, though I try to avoid that so that each system only deals with itself instead of keeping references to other systems. Sometimes though, there is no way around it.

[quote]
Exposing the aggregate functionality so that the entity was more than 'update this every frame or so' (unless you found that unnecessary)
[/quote]
I don't think I understand this either. An entity has no logic either. Systems update using my event manager. Either I run a system every certain intervals or immediately. For example, my rendering system has a octree sub-system to partition my space. When my even manager triggers my renderer to run @ an interval of 60fps, the renderer will ask the event manager to run the octree system once before rendering.


[quote][list=1]
[*]Read some data that defines a device.
[*]Using that data, populate the features that the device can support. There are ~300 features, and for the sake of discussion, they share no natural traits. They all have various behaviors that need to be set and otherwise twiddled, and tend to interact with one another. A given device will almost always have >100 features. About 8 of them are on every device.
[*]Allow new configs, and new features that can be deployed independently of existing ones.
[/list]
Right now, the existing code base is by and large a giant (both wide and deep) inheritance hierarchy, populated by string dictionaries and wired together with tin cans and string (generic ValueChanged events). This blows goats.

If a conventional OO design can do these things well, that would be awesome. But I don't see it. I also don't see how a component system can be implemented without getting into a giant grey gooey mess that is impossible to debug and fragile as hell.

[/quote]
I still have no idea what you're trying to do. What do you mean by device & features? Perhaps an entity system isn't the way to go? But without a clear understanding of what your goals are, I'm afraid I can't help much.

An entity system is useful for data driven applications. A game will have many assets and resources hence it is why an entity system is useful. A level designer or content creator can use tools. These tools create files that represent entities and their components. At load time, the engine parses the files and generates the entities and adds the correct components. It then registers everything, and if the systems are already in place to process these entities with these components, everything should run automatically. Edited by french_hustler

Share this post


Link to post
Share on other sites
[quote name='french_hustler' timestamp='1343280628' post='4963189']
What do you mean by device & features?
[/quote]

I am working on what amounts to a fancy sound simulator.

A [b]device [/b]is the physical hardware/environment that we're working with. The hardware itself can vary significantly. There might be more speakers, or less, in different configurations with different capabilities. I won't have many ever instantiated (usually 2), but there are a number of different platforms we work with.

A [b]feature[/b] represents something a device can do. To make the simulator go, there's a whole lot of knobs and switches that can be manipulated, and many of the knobs only exist for certain hardware (since many of the implementations are done in hardware). One feature might be adding wind noise to the environment. That has knobs for the speed of the wind, the air density (which is shared with other features), how much it shifts, etc. But it can't live in isolation. If we have a separate sound source, the wind causes different attenuation depending on where that sound is, and what frequencies it operates at.

The issue is that this one example is fairly easy to model in isolation. The wind noise is its own class that can be abstracted into a filter on input/output. Though that doesn't provide a solution for how consumers can access and manipulate the possibly non-existent feature nicely. And it isn't practical once things are not-necessarily filters and there's [i]hundreds [/i]of them.

Hopefully that helps you understand.

Share this post


Link to post
Share on other sites
You could let each component create a UI layout when it needs to show a UI part somewhere on screen. Another component attached to the entity can then pick these things up and collate them into a coherent UI for that entity.

This is going to be tricky however if UI capable components need to be able to attach and unattached at run time but should still be doable. I work with a system that does this all the input data for the UI does is describe a logical layout of the items on the screen and how you can navigate them; plus the behaviours attached to these items. How they are displayed is completely up to how the artists layout the art components. Off course our logical screen layout must more or less reflect this layout, but there is nothing stopping you from creating a system that does this for you based on the logical description instead of art layout.

Share this post


Link to post
Share on other sites

This topic is 1964 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this