Am I on the right track with this component architecture?

user127817
  • Am I on the right track with this component architecture? user127817

    I've recently decided to revamp my game architecture to get rid of deep class hierarchies and replace them with configurable components. The first hierarchy I'm replacing is the Item hierarchy and I would like some advice to know if I'm on the right track.

    Previously, I had a hierarchy that went something like this:

    Item -> Equipment -> Weapon
                      -> Armor
                      -> Accessory
         -> SyntehsisItem
         -> BattleUseItem -> HealingItem
                          -> ThrowingItem -> ThrowsAsAttackItem
    

    Needless to say it was starting to get messy and these was no easy solution to items that needed to be multiple types (i.e. some equipment is used in item synthesis, some equipment is throwable, etc.)

    I then attempted to refactor and place functionality into the base item class. But then I was noting that the Item had alot of unused/superfluous data. Now I'm trying to do a component like architecture, at least for my items before attempting to do so to my other game classes.

    Here's what I'm currently thinking for the component setup:

    I have a base item class that has slots for various components (i.e. an equipment component slot, a healing component slot, etc. as well as a map for arbitrary components) so something like this:

    class Item
    {
        //Basic item properties (name, ID, etc.) excluded
        EquipmentComponent* equipmentComponent;
        HealingComponent* healingComponent;
        SynthesisComponent* synthesisComponent;
        ThrowComponent* throwComponent;
        boost::unordered_map<std::string, std::pair<bool, ItemComponent*> > AdditionalComponents;
    } 
    

    All item components would inherit from a base ItemComponent class, and each Component type is responsible for telling the engine how to implement that functionality. i.e. the HealingComponent tells the battle mechanics how to consume the item as a healing item, while the ThrowComponent tells the battle engine how to treat the item as a throwable item.

    The map is used to store arbitrary components that are not core item components. I'm pairing it with a bool to indicate whether the Item Container should manage the ItemComponent or if it's being managed by an external source.

    My idea here was that I define the core components used by my game engine up front, and my item factory would assign the components that the item actually has, otherwise they are null. The map would contain arbitrary components that would generally be added/consumed by scripting files.

    My question is, is this a good design? If not, how can it be improved? I considered grouping all components into the map, but using string indexing seemed unecessary for the core item components

  • It seems like a very reasonable first step.

    You're opting for a combination of generality (the "additional components" map) and lookup performance (the hard-coded members), which may be a bit of a pre-optimization -- your point concerning the general inefficiency of string-based look-up is well-made, but you can alleviate that by choosing to index components by something faster to hash. One approach might be to give each component type a unique type ID (essentially you're implementing lightweight custom RTTI) and index based on that.

    Regardless, I would caution you to expose a public API for the Item object that allows you ask for any component -- the hardcoded ones and the additional ones -- in a uniform fashion. This would make it easier to change the underlying representation or balance of hardcoded/non-hardcoded components without having to refactor all the item component clients.

    You might also consider providing "dummy" no-op versions of each of the hard-coded components and ensure that they are always assigned -- you can then use reference members instead of pointers, and you will never need to check for a NULL pointer before interacting with one of the hard-coded components classes. You will still incur the cost of the dynamic dispatch to interact with that component's members, but that would occur even with pointer members. This is more of a code cleanliness issue because the performance impact will be negligible in all likelihood.

    I don't think it's a great idea to have two different kinds of lifetime scopes (in other words, I don't think the bool you have in the additional component map is a great idea). It complicates the system and implies that destruction and resource release isn't going to be terribly deterministic. The API to your components would be much clearer if you opted for one lifetime management strategy or the other -- either the entity manages the component lifetime, or the subsystem that realizes components does (I prefer the latter because it pairs better with the outboard component approach, which I will discuss next).

    The big downside I see with your approach is that you are clumping all of the components together in the "entity" object, which actually isn't always the best design. From my related answer to another component-based question:

    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.

    You're essentially taking the same approach by storing the components in the item entity (which is, again, an entirely acceptable first step). What you may discover is that the bulk of the access to components you are concerned about the performance of is just to update those, and if you elect to use a more outboard approach to component organization, where the components are kept in a cache-coherent, efficient (for their domain) data structure by a subsystem that understands their needs the most, you can achieve much better, more parallelizable update performance.

    But I point this out only as something to consider as a future direction -- you certainly don't want to go overboard overengineering this; you can make a gradual transition through constant refactoring or you may discover your current implementation meets your needs perfectly and there's no need to iterate on it.

Tags
c++ architecture component-based
Related questions and answers
  • my classes and still have them do what I want. Here's a few examples of a dependency chain: I have a status effect class. The class has a number of methods (Apply/Unapply, Tick, etc.) to apply... sort of designs other people used to overcome them, or would use. For a little background, I started working on it in my free time last summer. I was initially making the game in C#, but about 3 months ago, decided to switch to C++. I wanted to get a good handle on C++ since it's been awhile since I used it heavily, and figured an interesting project like this would be a good motivator. I've been

  • ; }; class Entity { int id; // entity id std::vector<Component*&gt; components; bool has_component() { return components[i] != 0; } template <class C> C* get_component() { return... remove_component(C* c) { components.at<C>() = 0; } void serialize(filestream, op) { /* Serialize all componets*/ } ... }; std::list<Entity*&gt; entity_list; With this design I can get #1...I'm writing a shooter (like 1942, classic 2D graphics) and I'd like to use a component based approch. So far I thought about the following design: Each game element (airship, projectile, powerup

  • /////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Sprite { private: string name; char symbol; float shield; int location[2]; bool alive; public: ///////////////////// Get and SET all the privates...Alright so i'm making a vertical side scroller where you are an '8' character traveling downward while avoiding my randomly generated walls (the "generation" function) on the left and right sides... in the main function * not Finally.. but i believe that te numb_coll problem has something to do with the time and framrate. When running full speed i quickly die upon entering a wall as numb_coll

  • well... I'm building the animation system of my game engine (the skeletal and skinned animation stuff), and I came to a point where I added so much functionality in the frame and node structures... something that will be default values in most of the scene objects (I believe the scene won't be built from skinned hierarchy meshes but mostly static and even in some cases the animation is purely... animation I would end up storing a lot of data that I don't really need, even if I'm indexing node and frame data when saving and then store the hierarchy with the indices to the actual data. I don't

  • components total. Or is this even feasible? How much I can accomplish with this approach? In essence I want to be able to do something like this with reasonable efficiency: moving_update(DB...I am studying entity indexed components and came up with a naive C++ implementation which just iterates over all entity "hash tables" and applies update/delete/insert functions in place. I'm having trouble maintaining a logical view of the game world (i.e. updates shouldn't be visible while iterating) and there is no attempt at maintaining spatial or temporal coherence of data. I wonder whether

  • I'm creating a component-based game object system. Some tips: GameObject is simply a list of Components. There are GameSubsystems. For example, rendering, physics etc. Each GameSubsystem contains... 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... 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

  • I've decided I want to write a central ResourceManager/ResourceCache class for my hobby game engine, but am having trouble designing a caching scheme. The idea is that the ResourceManager has... ~Resource(); virtual bool load() = 0; virtual bool unload() = 0; virtual size_t getSize() = 0; // Used in determining how much memory is // being used. bool... can't think of a decent scheme to use, or the right data-structures required to quickly manage them. Could someone who has implemented a system like this give an overview of how their's worked

  • physics properties. I know there will be errors here as I get the items colliding with the ground but not with each other properly. So I would appreciate some input on what I am doing wrong. void... this on what I had done there, but modifying it to fit with Ogre. It is working but not correctly and I would like some help to understand what it is I am doing wrong. I have a state system and when... code shows the changes I made to get accurate physics. void GameState::createScene() { m_pSceneMgr-&gt;createLight("Light")-&gt;setPosition(75,75,75); // Physics // As a test we want a floor plane

  • I want to be able to (only) define game states using Lua script, but I'm not sure how I should do it. Here's what I have in mind currently: For each state, I will create a .lua file that contains... exiting the state). So if I want to have a MainMenuState, I will have a file called "MainMenuState.lua" which will contain something like this: MainMenuState = {} MainMenuState["onEnter"] = function() end MainMenuState["onUpdate"] = function(elapsedTime) end MainMenuState["onExit"] = function() end Defined states will be exposed to the game engine via a singleton StateManager class

Data information