7
\$\begingroup\$

I was thinking of how to implement overriding of behaviors in a component based entity system. A concrete example, an entity has a heath component that can be damaged, healed, killed etc. The entity also has an armor component that limits the amount of damage a character receives.

Has anyone implemented behaviors like this in a component based system before?
How did you do it?

If no one has ever done this before why do you think that is. Is there anything particularly wrong headed about overriding component behaviors?

Below is rough sketch up of how I imagine it would work. Components in an entity are ordered. Those at the front get a chance to service an interface first. I don't detail how that is done, just assume it uses evil dynamic_casts (it doesn't but the end effect is the same without the need for RTTI).

class IHealth
{
public:
   float get_health( void ) const = 0;
   void do_damage( float amount ) = 0;
};

class Health : public Component, public IHealth
{
public:
   void do_damage( float amount )
   {
      m_damage -= amount;
   }
private:
   float m_health;
};

class Armor : public Component, public IHealth
{
public:
   float get_health( void ) const
   {
      return next<IHealth>().get_health();
   }

   void do_damage( float amount )
   {
      next<IHealth>().do_damage( amount / 2 );
  }
};

entity.add( new Health( 100 ) );
entity.add( new Armor() );
assert( entity.get<IHealth>().get_health() == 100 );
entity.get<IHealth>().do_damage( 10 );
assert( entity.get<IHealth>().get_health() == 95 );

Is there anything particularly naive about the way I'm proposing to do this?

\$\endgroup\$
3
  • \$\begingroup\$ Please see my answer here: gamedev.stackexchange.com/questions/13916/…. If linking to one own answers is frowned upon, please mods remove this. \$\endgroup\$
    – raine
    Commented Jun 20, 2011 at 7:14
  • 2
    \$\begingroup\$ Is there a specific reason why you create an extra interface for IHealth and then use multiple inheritance further down instead of having IHealth inherit from Component and then have Armor and Health inherit from IHealth alone? \$\endgroup\$
    – TravisG
    Commented Jun 20, 2011 at 8:25
  • \$\begingroup\$ @heishe: I was worried that a component may want to implement two disparate interfaces, ie IHealth and IKnockback. It would not make sense to join those two components in a single class hierarchy. Multiple inheritance is always troublesome I had considered having Shield use a proxy member class that derives from IHealth and then forwards all calls to Shield. With that implementation technique there is no MI at the expense of an extra non-virtual method call (which the optimizer may be able to inline). In either case the API (add, get, next, etc) is the same. \$\endgroup\$
    – deft_code
    Commented Jun 20, 2011 at 14:19

4 Answers 4

3
\$\begingroup\$

I think you're making it a little too complicated, or not complicated enough.

One direction I would suggest would be to break apart getting health and doing damage interfaces.

So maybe as an idea, your Armor component would look like this:

class Armor : public Component, public IDamageReceiver
{
public:
   void do_damage( float amount )
   {
      // just assume that subtract_health isn't used in client code maybe
      this.get<IHealth>().subtract_health( amount / 2 ); 
   }
};

Alternatively (and probably what I would do), you could make your components a little bigger and just have a generic "Entity" base class that your specific types derive from. This would contain the functionality of both health and armor and you could implement specific entities from that.

\$\endgroup\$
3
  • \$\begingroup\$ Your solution doesn't quite address my problem. I want to add the Armor component without adjusting any of the pre-existing components. Done right, I think you could just add two armor components and it would do the right thing (reduce damage to 25%). In other words with a general override solution I could override the overrides. \$\endgroup\$
    – deft_code
    Commented Jun 20, 2011 at 5:48
  • 3
    \$\begingroup\$ I kind of agree with Tetrad here. I think you're making your system way too complex and granular. I would say if you want to override component X, find component X in the relevant entities and swap it out with component Y that does what you want. \$\endgroup\$
    – user1430
    Commented Jun 20, 2011 at 14:56
  • 1
    \$\begingroup\$ @deft_code Based on what you said and your other post about components before this one. I think you want to introduce a 'CharacterEffects' (not using power ups as they may be bad at times :)) type of component where you could create (de)buffs to apply to a character. This will still require the health component to be updated to scan for associated effects to see if any apply, but it comes down to needing a new component type rather than inheriting from health and Not replacing health. Alternately, make your component system have a stack so there is guaranteed order of execution/resolution. \$\endgroup\$
    – James
    Commented Jun 22, 2011 at 23:10
2
\$\begingroup\$

Your thoughts are in the right place but I think you are trying to take the component system to a level of detail that it does not need. I would say make a component called Health when all you need is straight up health. But if you need a health component that also has armor in it, then make it a CHealthAndArmor component that still fits the API of Health in general so it can be used like any other, but then provide the manner of also changing the armor levels if needed.. If its completely a load time value then there is no need, that's just the internal workings of that particular health component.

Honestly the concerns you are having (from this and your other post) are how fine a level of detail you break components down into. If you ever find yourself inheriting or encapsulating a component within another component, you should reconsider what you are doing. You generally are trying to avoid inheritance like this in component based architectures.

Hope this helps

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Making a HealthAndArmor component kind of defeats the purpose of a component based architecture. \$\endgroup\$
    – Mitchell
    Commented Nov 20, 2012 at 8:17
  • \$\begingroup\$ @Mitchell That all depends on the game really. If your armor is nothing more than extra health like in the old Doom/Quake style games then splitting these things apart would be a complete waste. Your components should match your environment and game. \$\endgroup\$
    – James
    Commented Nov 20, 2012 at 17:21
  • \$\begingroup\$ I disagree, splitting them allows for easy modification in the future, and the change from 'armor and health' to 'health' only requires the removal of armor, instead of the removal of HealthAndArmor and the adding of Health. It might seem insignificant, but it can become really annoying when you're prototyping a lot. But I guess it also depends on your entity manager. \$\endgroup\$
    – Mitchell
    Commented Nov 27, 2012 at 8:15
  • \$\begingroup\$ @Mitchell I guess we just will have to disagree then.. I have always seen components as something you tailor to the game/application in question. Sure you gather a nice collection of them over time depending on how many projects you use/reuse the system for, but the entire point of component system to me is to create more complicated objects out of simple and specific smaller objects. \$\endgroup\$
    – James
    Commented Nov 27, 2012 at 18:02
2
\$\begingroup\$

Your Component stuff is too much OOP. See what I'm doing here:

https://github.com/thelinuxlich/starwarrior_CSharp

And this is the Entity System framework I've ported to C#: https://github.com/thelinuxlich/artemis_CSharp

\$\endgroup\$
0
\$\begingroup\$

I use an event driven component system and had this same issue. Basically I subscribe component functions to other component events. For one event I can have x functions subscribed and events are fired in the order they were subscribed. Events also give one parameter which is a dynamic class and passed by reference. All of this means I can create a Stats component and a Health component. I subscribe both to a hurt message but because Stats is subscribed first it'll use the armor stat of the Stat component and modify the damage value that is inside the parameter object. The health component is next in the subscription list and by the time it is called the damage value has already been reduced.

I can't talk up enough the event waynof dealing with components. It really does allow piecing together functionality via components where they don't event know about each other. The user is just setting up the event subscriptions to build up the functionality. Would never look back from this system.

\$\endgroup\$

You must log in to answer this question.

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