views:

43

answers:

1

I'm creating a component-based game object system. Some tips:

  1. GameObject is simply a list of Components.
  2. There are GameSubsystems. For example, rendering, physics etc. Each GameSubsystem contains pointers to some of Components. GameSubsystem is a very powerful and flexible abstraction: it represents any slice (or aspect) of the game world.

There is a need in a mechanism of registering Components in GameSubsystems (when GameObject is created and composed). There are 4 approaches:


  • 1: Chain of responsibility pattern. Every Component is offered to every GameSubsystem. GameSubsystem makes a decision which Components to register (and how to organize them). For example, GameSubsystemRender can register Renderable Components.

pro. Components know nothing about how they are used. Low coupling. A. We can add new GameSubsystem. For example, let's add GameSubsystemTitles that registers all ComponentTitle and guarantees that every title is unique and provides interface to quering objects by title. Of course, ComponentTitle should not be rewrited or inherited in this case. B. We can reorganize existing GameSubsystems. For example, GameSubsystemAudio, GameSubsystemRender, GameSubsystemParticleEmmiter can be merged into GameSubsystemSpatial (to place all audio, emmiter, render Components in the same hierarchy and use parent-relative transforms).

con. Every-to-every check. Very innefficient.

con. Subsystems know about Components.


  • 2: Each Subsystem searches for Components of specific types.

pro. Better performance than in Approach 1.

con. Subsystems still know about Components.


  • 3: Component registers itself in GameSubsystem(s). We know at compile-time that there is a GameSubsystemRenderer, so let's ComponentImageRender will call something like GameSubsystemRenderer::register(ComponentRenderBase*).
    Observer pattern. Component subscribes to "update" event (sent by GameSubsystem(s)).

pro. Performance. No unnecessary checks as in Approach 1 and Approach 2.

con. Components are badly coupled with GameSubsystems.


  • 4: Mediator pattern. GameState (that contains GameSubsystems) can implement registerComponent(Component*).

pro. Components and GameSubystems know nothing about each other.

con. In C++ it would look like ugly and slow typeid-switch.


Questions: Which approach is better and mostly used in component-based design? What Practice says? Any suggestions about (data-driven) implementation of Approach 4?

Thank you.

+1  A: 

Vote for the third approach.

I am currently working on component-based game object system and i clearly see some of additional advantages of this approach:

  • The Component is more and more self-sufficing subentity as it depends only on a set of available subsystems (i presume this set is fixed in your project).

  • Data-driven design is more applicable. Ideally, this way you may design a system where components are completely defined in the terms of data but not C++.


EDIT: One feature i thought about while working on CBGOS. Sometimes it is convenient to have ability to design and construct subsystemless passive components. When this is on your mind the fourth approach is the only way.

Keynslug
I agree with you that these advantages are very valuable. But the first one has another side: no `Component` reusability between projects (with different sets of `Subsystems`). Recomposition of subsystems in a single project becomes an issue, too. There can be hundreds of `Components`, rewriting them all is a tedious task. I believe that second advantage can be reached in other approaches, too.
topright
Agree with your point. At a time i am introduced with CBGOS design not as much as i wish to. But the work i ran into dealing with this got me to the following considerations: *1.* Design subsystem interfaces at the most abstract way so the set of subsystems would change slightly across different projects. *2.* Prefer by-message interaction between components above all and cut out interface dependencies where it's possible.
Keynslug
It can work out. I met people at gamedev.net forums that use this approach, too.
topright
About "subsystemless passive components". I found extremely useful to divide `Components` into 3 different hierarchies: Model, View, Controller. ModelComponent - contains just data and some useful routines. (ComponentTitle mentioned in the question for example) ControllerComponent has method update and performs something at every loop. ViewComponents - for rendering, audio etc. There are 3 corresponding `Subsystem` types. We know at compile-time what kind of `Component` we are searching for in the `GameObject`, so searching becomes 3 times faster. And we achieve a better decomposition.
topright
Did not think this way about components decomposition. It must be valuable.
Keynslug