Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Register Game Object Components in Game Subsystems? (Component-based Game Object design)

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.

like image 553
topright gamedev Avatar asked Oct 18 '10 10:10

topright gamedev


2 Answers

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.

like image 199
Keynslug Avatar answered Sep 28 '22 06:09

Keynslug


My approach was to implement the proxy pattern within each subsystem. As each subsystem is only interested in a subset of the total components each entity may contain, The proxy stores pointers to only the components the system cares about, eg, a motion system only cares about position and velocity, so it needs a proxy which stores two pointers, to those components. If the entity is missing one or more of those, then the subsystem will ignore it. If both components are present, then a proxy node is created and added to an internal collection. It is also useful for the proxy to store the unique identifier value for the entity, so that proxies may be added/removed in constant time from each subsystem, should it be necessary.

In such a way, should an entity be required to be removed from the engine, a single message containing the id of the entity can be sent to every subsystem. The proxy can then be removed from each subsystem collection independently.

like image 35
Ian Young Avatar answered Sep 28 '22 08:09

Ian Young