19
\$\begingroup\$

I have spent the last 48 hours reading up on Object Component systems, and feel I am ready enough to start implementing it. I got the base Object and Component classes created, but now that I need to start creating the actual components I am a bit confused. When I think of them in terms of HealthComponent or something that would basically just be a property, it makes perfect sense. When it is something more general as a Physics/Graphics component, I get a bit confused.

My Object class looks like this so far (If you notice any changes I should make please let me know, still new to this)...

typedef unsigned int ID;

class GameObject
{
public:

    GameObject(ID id, Ogre::String name = "");
    ~GameObject();

    ID &getID();
    Ogre::String &getName();

    virtual void update() = 0;

    // Component Functions
    void addComponent(Component *component);
    void removeComponent(Ogre::String familyName);

    template<typename T>
    T* getComponent(Ogre::String familyName)
    {
        return dynamic_cast<T*>(m_components[familyName]);
    }

protected:

    // Properties
    ID m_ID;
    Ogre::String m_Name;
    float m_flVelocity;
    Ogre::Vector3 m_vecPosition;

    // Components
    std::map<std::string,Component*> m_components;
    std::map<std::string,Component*>::iterator m_componentItr;
};

Now the problem I am running into is what would the general population put into Components such as Physics/Graphics? For Ogre (my rendering engine) the visible Objects will consist of multiple Ogre::SceneNode (possibly multiple) to attach it to the scene, Ogre::Entity (possibly multiple) to show the visible meshes, and so on. Would it be best to just add multiple GraphicComponent's to the Object and let each GraphicComponent handle one SceneNode/Entity or is the idea to have one of each Component needed?

For Physics I am even more confused. I suppose maybe creating a RigidBody and keeping track of mass/interia/etc. would make sense. But I am having trouble thinking of how to actually putting specifics into a Component.

Once I get a couple of these "Required" components done, I think it will make a lot more sense. As of right now though I am still a bit stumped.

\$\endgroup\$

2 Answers 2

33
\$\begingroup\$

First of all, you when build component-based systems, you don't have to take the approach of turning everything into a component. In fact, you generally shouldn't -- it's something of a neophyte mistake. In my experience the best way to tie together rendering and physics systems in a component based architecture is to make those components little more than dumb containers for the real rendering or physics primitive object.

This has the added advantage of keeping the rendering and physics systems decoupled from the component system.

For physics systems, for example, the physics simulator generally keeps a big list of rigid body objects that it manipulates every time it ticks. Your component system does not mess with that -- your physics component simply stores a reference to the rigid body that represents the physical aspects of the object the component belongs to, and translates any messages from the component system into appropriate manipulations of the rigid body.

Likewise with rendering -- your renderer will have something representing the instance data to be rendered, and your visual component simply holds on to a reference to that.

It's relatively straightforward -- for whatever reason it seems to confuse people, but that's often because they are thinking about it too hard. There's not a lot of magic there. And since it's most important to get things done rather than agonize over the perfect design, you should strongly consider an approach where at least some of the components have defined interfaces and are direct members of the game object, as momboco has suggested. It's just as valid an approach and it tends to be easier to grok.

Other Comments

This is unrelated to your direct questions, but these are things I noticed when looking at your code:

Why are you mixing Ogre::String and std::string in your game object, which is ostensibly a non-graphics object and thus should not have a dependency on Ogre? I would suggest removing all direct references to Ogre except in the rendering component itself, where you should do your transformation.

update() is a pure virtual, which implies that one must subclass GameObject, which is actually kind of exactly the opposite of the direction you want to go in with a component architecture. The main point of using components is to prefer aggregation of functionality rather than inheritance.

You could benefit from some use of const (particular where you are returning by reference). Also I'm not sure that you really want velocity to be a scalar (or if it, and position, should be present in the game object in the kind of overly generic component methodology you seem to be going for).

Your approach of a using a big map of components and an update() call in the game object is quite sub-optimal (and a common pitfall for those first building these sorts of systems). It makes for very poor cache coherency during the update and doesn't allow you to take advantage of concurrency and the trend towards SIMD-style process of large batches of data or behavior at once. It's often better to use a design where the game object doesn't update its components, but rather the subsystem responsible for the component itself updates them all at once. This might be worth a read.

\$\endgroup\$
9
  • \$\begingroup\$ This answer was excellent. I still haven't figured out a way to space paragraphs in comments (enter key just submits) but I will address these responses. Your description of the Object/Component system made a lot of sense. So just to clarify, in the case of a PhysicsComponent, in the component's constructor, I could just call my PhysicsManager's CreateRigidBody() function, and then store a reference to that in the component? \$\endgroup\$ Commented Jun 17, 2011 at 15:16
  • \$\begingroup\$ As far as the "Other Comments" portion, thanks for this. I mixed strings because I generally prefer to use all Ogre functions as a basis for my project's that use Ogre, but I started switching it up in the Object/Component system since it lacked map/pair/etc. You are right though and I have converted them all over to the std members to seperate it from Ogre. \$\endgroup\$ Commented Jun 17, 2011 at 15:18
  • 1
    \$\begingroup\$ +1 That's the first sane answer concerning a component based model I read here in a while, nice contrast against over-generalizing \$\endgroup\$ Commented Jun 17, 2011 at 18:52
  • 5
    \$\begingroup\$ It allows for better coherency (both of data and in the instruction cache) and affords you the possibility of almost trivially offloading the updates onto distinct threads or worker processe (SPUs, for example). In some cases the components wouldn't even need to be updated anyhow, which means you can avoid the overhead of a no-op call to an update() method. Also helps you build data-oriented systems -- it's all about bulk processing, which takes advantage of modern trends in CPU architecture. \$\endgroup\$
    – user1430
    Commented Jun 19, 2011 at 22:21
  • 2
    \$\begingroup\$ +1 for "it's most important to get things done rather than agonize over the perfect design" \$\endgroup\$ Commented Sep 16, 2011 at 7:22
5
\$\begingroup\$

The component architecture is an alternative to avoid the large hierarchies that are achieved inheriting all elements from a same node, and the problems of coupling that leads.

You are showing a component architecture with "generic" components. You have the basic logic to attach and detach "generic" components. An architecture with generic components is more difficult to achieve because you don't know which component is. The benefits is that this approach is extensible.

A valid component architecture is achieved with defined components. With defined I mean, the interface is defined, not the implementation. For example, GameObject can have a IPhysicInstance, Transformation, IMesh, IAnimationController. This has the benefit that you know the interface and the GameObject knows how to deal with each component.

Responding to the term over-generalizing

I think that the concepts are not clear. When I say component, doesn't mean that all component of a game-object must inherit from a component interface or something similar. However this is the approach for Generic Components, I think that it is the concept that you name as a component.

The component architecture is another of the architectures that exist for runtime object model. It's not the only and its not the best for all cases, but the experience has demonstrated that has a lot of benefits, like low coupling.

The main feature that distinguishes the component architecture is convert the relation is-a into has-a. For example a object architecture is-a:

is-a architecture

Instead of a object architecture has-a ( components ):

has-a architecture

There is also property-centric architectures :

For example, a normal object architecture is like this:

  • Object1

    • Position = ( 0, 0, 0 )
    • Orientation = ( 0, 43, 0 )
  • Object2

    • Position = ( 10, 0, 0 )
    • Orientation = ( 0, 0, 30 )

A property-centric architecture:

  • Position

    • Object1 = ( 0, 0, 0 )
    • Object2 = ( 10, 0, 0 )
  • Orientation

    • Object1 = ( 0, 43, 0 )
    • Object2 = ( 0, 0, 30 )

It is better the object model architecture? In performance perspective, it's better the property-centric because it is more cache-friendly.

Component is refered to the relation is-a instead of has-a. A component can be a Transformation matrix, a quaternion, etc. But the relation with the game-object is a relation is-a, the game-object doesn't have the transformation because it's inherited. The game-object has ( relation has-a ) the transformation. The transformation is then a component.

The game-object is like a hub. if you want a component that always is there, then maybe the component ( like the matrix ) should be inside the object and not a pointer.

There is not a perfect game-object for all cases, it depends the engine, the libraries that the engine use. Maybe I want a camera that doesn't have a mesh. The problem with is-a architecture is that if the game-object has a mesh, then the camera that inherits from game-object must have a mesh. With componenets, the camera has a transformation, a controller for the movement, and anything you need.

For more information, the chapter "14.2. Runtime Object Model Architecture" from the book "Game Engine Architecture" of "Jason Gregory" deals specifically this subject.

UML diagrams are extracted from there

\$\endgroup\$
9
  • \$\begingroup\$ I disagree with the last paragraph, there is a tendency of over-generalizing. Component based model does not mean that each and every functionality must be a component, still one must consider relationships of functionalities and data. An AnimationController without a Mesh is useless, so put it where it belongs to, into the mesh. A Game-Object without a transform is not a Game-Object, it belongs by definition into the 3D world, so put it as a normal member into the Game-Object. The normal rules of encapsulation functionality and data still apply. \$\endgroup\$ Commented Jun 17, 2011 at 19:15
  • \$\begingroup\$ @Maik Semder: I'm agree with you in the over-generalizing in general terms, but I think that the term component is not clear. I've edited my answer. Read it and give me your impressions. \$\endgroup\$
    – momboco
    Commented Jun 18, 2011 at 1:39
  • \$\begingroup\$ @momboco well, you more less repeated the same points ;) Transform and AnimationController are still described as components, which in my point of view is an overuse of the component model. The key is to define what should be put into a component and what not: \$\endgroup\$ Commented Jun 18, 2011 at 9:43
  • \$\begingroup\$ If something is needed to make the Game-Object (GO) work in the first place, in other words if it's not optional, but mandatory, then it is not a component. Components are supposed to be optional. The Transform is a good example, it is not optionl at all for a GO, a GO without a Transform makes no sense. On the other hand, not every GO needs physics, so that can be a component. \$\endgroup\$ Commented Jun 18, 2011 at 9:44
  • \$\begingroup\$ If there is a strong relationship between 2 funtionalities, as between AnimationController (AC) and Mesh, then by any means don't break this relationship by pressing the AC into a component, in contrast the Mesh is a perfect component, but an AC belongs into the Mesh. \$\endgroup\$ Commented Jun 18, 2011 at 9:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .