Wednesday, April 30, 2014

Component system

This time some more technical post about components system that I work on. So lets start from begin :)

Making decision about switching to components took me almost year. Seem like pretty long time to rethink everything. But not necessarily when you need to change big part of your code base. So I focused on finding all pros and cons behind new system. In meantime I had a lot of talks with programmers friends in Poland and here in Canada. Thanks to them I was able to create my own opinion and vision what I want to achieve with this new code.



Why the hell I needed component

I don't see any game engines based on inheritance of game classes as something bad. My engine was like that from begin and I liked it to some point. But then few problems shown:
  • Extending of basic engine functionality by game is not easy.
  • The ease with which code start to be messy.
Which in the fact is the one problem. You struggle with extending code and because of that everything start to be messy when your code base grow. I don't believe component are't magic potion that will fix all problems. But with this two it helped me for sure.

Long time ago behind seven inheritances

It happened because I needed to change my coding "scope". Earlier I would do code like this:
CModelObject : public CGameObject

{
    WR_RTTI(CModelObject, CGameObject);
public
    ....
private:
    IPhysics*             m_physics;
    IMeshInstance*   m_mesh;
    ...
};
class CLbaSomeObject : public CModelObject
{
    WR_RTTI(CLbaSomeObject, CModelObject);
public
    ....
private:
    ILbaSomeData* m_data;
    ...
};
Right now I do only: 
class CLbaSomeData : public IGameObjectComponent    WR_RTTI(CLbaSomeData, IGameObjectComponent);public     .... private:    ILbaSomeData* m_data;    ...};
Look similar but (there always need to be: but) when I writing old code I was very often thinking how to implement this feature in this particular class. Simple, clean and needed very often to be changed when you add inheriting class. This small stuff force you very often to shuffling of code to allow overrides or change the order of initialization and things start to get messy when you have very depth inheritance tree.

And because games need a lot of game specific stuff very often this is the case. So code grow very fast and need additional effort to make it clean. In case of components I always need to think about two things: it can be use in any game objects and in any place of code somebody can call:
if (CLbaSomeData* data = a_object->getComponentT())
{
    //.. here we do something
}
So the component code need to be very generic and encapsulated to it's limits.

Game blocks

So I started to do transition and organizing code in component like way. When I finished I had nice building blocks but there was no generic way of storing them in objects.

I decided on nothing fancy just simple static table of pointers. Right now I use maximum 5 components per object so I decided on 8 slots. I know that it may be to little in some cases and too much in others. That is the reason why I think about mechanism that allow me to use 4-32 of them but this is future.

I was satisfy with results but I had another problem. Lets look i.e at AI in game:  In L.B.A. remake I decided partially centralized management of it so I have :

When I create AI component I need to attach object to manager. Because I want manager to be created per level I needed to make it accessible from component. Of course I wanted the design that will allow creating any system in similar way. So (here surprise) I introduced LevelComponent :]

I found this solution in meantime of discussion with friend why I force somebody to use physics in my engine. Advantages of this solution is that if I want to add some game manager to level I use it exactly the same way as any other stuff.

Components internal mechanism

Of course all this step would be nothing without figuring out how I want to manage components update. And there is a lot of different ways to resolve this problem. But because it is the first iteration over system I need to limit myself.

I decided to do it in very basic way to make everything work quickly. So right now:
  • Each component is update separately
  • Some base communication between components use events.
By base communication I mean broadcasting information like :
  • component attached, 
  • component detached, 
  • component changed.
Of course this is not final form of it and I think about doing some more efficient techniques. Like batched update of components which was described in one of Naughty Dog presentation. But this is future.

Futurama

And future look better even if it's not ideal. Rewriting technology so it support components is not easy. You need to:
  • Find new way of dealing with problems that in previous environment would be easy.
  • Throw out a lot of code and even more of it change.
But after you finish doing it you see so much new possibilities and think that all this effort was worth it. 

2 comments:

  1. How do you handle other (not "base") communication between components (as in most of gameplay-related communication, like damage etc)? By talking to components directly or rather by events/messages (like there: www.gdcvault.com/play/1911/‎ )?

    I thought about moving my pet project to component system and there are couple of things to think about:
    1. Is there a need to have "game object" class, or all that's needed to identify an object is its numerical ID (then one could request access to components by this ID)
    2. Should components be updated SoA or AoS-like? Most gameplay logic will require access to data from multiple components, so updating all instances of single component type at once can in fact result in poorer performance (not that it's so important in a pet project)

    ReplyDelete
  2. Personally I use direct usage of component which should be enough for my current needs. But if this change I will need to figure out something else.

    1. In the fact I see both solutions as very similar in use. At least to some point. In most cases for GameObjects you still need to use some ID to find it on level. Bigger difference is when you already find it. I for example decided to store pointers to components only inside CGameObjects or other components from the same object instance. Thanks to that when I register object in some managers I have quick access to all it's components. For solution with ID you would need to ask some global manager which I think could be slower for often usage or take a lot of memory when you search them using hashtables.

    2. I had really long discussion about efficient update of components with my friend Aaron. In the end I think that there are some basics components that can be updated in batches more efficient. Mostly because you can use more efficient cache and SSE extensions. But I agree that this rule don't apply to all of them.

    ReplyDelete