I do like the point that by using a component model the problem of game-implementation-complexity isn't magically solved. Often times the game complexity falls onto the communication between the more modular pieces. It's fairly safe to say composition-style does aid in dismantling inflexibility in areas that inheritance trees, I'm sure we all mostly agree on this.
However it looks like there's often an "assumption" on the internet that communication/messaging will also magically align itself with the stars. Another assumption is that all of the entirety of a game engine ought to be forcefully shoved into a component model. As an alternative to these assumptions I would propose:
- Architect a couple ways to go about communication across the game. Pointers, function calls, virtual dispatch, polling, listeners/messages, etc. are some options. Each option needs to be considered and weighed against alternatives.
- Allow engineers to write components in a more traditional OOP-style in terms of API/interface. An OOP-style interface (i.e. class with member functions) does not incur hardly any overhead other than pointer aliasing. Lastly, the component API does not need to strictly reflect how the components are stored in memory.
- Clearly define where the component/aggregation will be used, and what the boundaries are. Are the components mostly interfaces to lower-level services (like wrappers to a graphics or physics API)? Are the components strictly for dynamic gameplay objects, or is every triangle in the world represented in the component model?
The point I'm attempting to argue for is that game implementation (like game logic, ai, state machines, interactions, scripts, etc.) will always be like an exploding plate of spaghetti. Each game is complex and vastly unique from every other game, with lots of inter-tangled bits. I argue to allow the game implementation to take on its most natural form. From an engineer's point of view this can be thought of as taking the path of least resistance -- instead of forcing the game to adhere to a certain paradigm in a strict manner, we let the game land where it will.
That said, pain can be taken to ensure the spaghetti explosion is as small as possible. This is where services like Direct X, or Box2D come into play. These services attempt to provide an API that solves a specific problem. The thing about APIs is that they are incredibly difficult to implement and require solid engineering. Component-models will require extremely solid API design in order to facilitate the demands of an exploding spaghetti game. As long as these APIs are constructed in a way that allows the game to latch onto the API without compromising its natural game-form, the API has succeeded. As Muratori explains in that last link, a good way to construct an API is to provide a range of lower-level "immediate mode" functions to higher-level "retained mode" functions. This range of options, that all ultimately solve the same specific problem, can help the game retain its natural form while latching onto the API for help.
Here's some quick justifications for each point I proposed:
1) This is an attempt to provide a range of communication options for whatever the team's needs are, in order to construct a solid API. I would recommend culling options that do not make sense for a given project.
2) Writing good APIs is super difficult, and when someone constructs a component they are in a way creating an API that encapsulates the solution to a problem. In general devs are familiar with OOP-ish APIs involving classes and member functions. Perhaps for different teams a different style for implementing components would work. This is just one idea. The assumption I made here is that a solid engineer has already implemented the mechanisms for creating/storing components efficiently, and another engineer (possibly the same one) is later on using this mechanism to implement game-specific components.
3) A component-model ought to solve a problem. I'd advise defining what problems the components will solve and then engineer the solution to those problems. Nothing else. For example, if components are meant only to store AI-scripts for run-time game entities then many simplifications can be made here to create a solution. Perhaps an array of function pointers to scripts is all that a "game object" needs to be, where each function pointer represents a script. Perhaps another array can be used to store parameters to each corresponding script index. Additional complexity in the component-model should only arise to meet the challenges of the solutions that the component-model is trying to solve.