3

I had a question about how to handle this type of class hierachy. The hierarchy is related to a game, but I think it's general enough to go here over the gamdev. I'd imagine this sort of thing comes up occasionally so I'm curious about the possible solutions

I have a base class called BaseCharacter that defines a character in my game. It contains all the basic stats, status effect holders, events etc. to have a baseline character. Most of my game logic operates on entities of BaseCharacter, casting where necessary.

From there I inherit into a Humanoid class which adds the necessary structures and methods to add equipment (weapons, armor, etc.) to the character, then finally I have a PlayableCharacter class that inheirts from humanoid and adds things like current EXP, configurable ability structures, etc.

So the class structure is simply:

BaseCharacter--->Humanoid--->PlayableCharacter

This structure works great for defining player side characters, though I may decide to refine the structure a little more. I have to be able to add "enemy" classes to the hierarchy. In order to satisfy the requirements for an enemy, I need to add some data structures/methods related to EXP/points for defeating the enemy, loot tables, etc.

The problem is, where do I add these structures? Adding them to BaseCharacter seems like a bad idea as I'm basically saying every character has the required methods/data structures for an enemy entity. I considered starting a separate stream of classes that would end up looking like this:

BaseCharacter--->Humanoid--->PlayableCharacter
           \---->Enemy--->EnemyHumanoid

where Enemy inherits from BaseCharacter. EnemyHumanoid would add the same set of methods/data structures as the inheritance from BaseCharacter->Humanoid. THis works, but means I can't cast EnemyHumanoid into Humanoid despite the fact that EnemyHumanoid satisfies the requirements of Humanoid. This also adds redundancy as I'd have these two inheritances add the exact same set of objects/methods.

then in considered using a seperate class called EnemyInterface, and end up with an inheritance hierarchy like this:

BaseCharacter----->Humanoid----->PlayableCharacter
    |                 |                
    V                 V                
  Enemy         EnemyHumanoid   

Enemy and EnemyHumanoid all implement EnemyInterface. However, I now lose the Enemy<-->EnemyHumanoid relationship. Should I use multiple inehritance here? (have EnemyHumanoid inheirt from Enemy and Humanoid?) is that considered bad form? Is there a more logical way to do this? The example here is rather simple, but if i do end up splitting my class hierarchy more, it'll only get more complicated.

Any help would be appreciated

Megatron
  • 2,871
  • 4
  • 40
  • 58

6 Answers6

2

Rather than rearranging your class hierarchy to solve this specific problem (see StackUnderblow's answer to see how to do that) I would suggest that you do some research into a component based architecture. Basically, instead of having a class that describes the things that make a PlayableCharacter unique, you would have a component (or set of components) grafted on to some kind of base container.

This approach is much more flexible and will be easier to refactor as your design evolves. For instance, if you have a feature that both some Enemies and Humans are supposed to be able to access, then you will have to add the system hooks to your base class, introducing overhead to the enemies that cannot use the feature. That's a lot of words to describe a simple problem, but you will have the problem over and over again as your game evolves.

Here is a good SO discussion expanding on my suggestion.

Bheeshmar is right, you are on the path to hell!

Community
  • 1
  • 1
Dan O
  • 4,323
  • 5
  • 29
  • 44
1

It sounds like you are using inheritance for reuse instead of adhering to LSP (http://en.wikipedia.org/wiki/Liskov_substitution_principle), which is the Path to Hell.

My general advice is to use inheritance only for is-a relationships. That is, where you can substitute one type for another. I prefer composition to inheritance for most solutions, as I have found it to be simpler.

In your specific case, why consider Enemy to be different than PlayableCharacter? Consider having the EXP and Loot Tables outside the class heirarchy where you can pass a Humanoid to it and calculate these items separately.

C++ is a multi-paradigm language. You don't have to confine yourself to OO solutions.

Good luck!

bheeshmar
  • 3,125
  • 1
  • 19
  • 18
1

How about this possible one ?

  BaseCharacter----->Humanoid----->PlayableCharacter
                      / \                
                     /   \                
                Enemy     EnemyHumanoid   

EnemyHumanoid might need only some features from Humanoid.

The Unknown
  • 146
  • 8
1

You're trying to subclass your BaseCharacter class according to multiple aspects (err, I'm not sure "aspect" is the right word here, I'm not a native English speaker, sorry), namely:

  • Form - Humanoid / Animal / etc.
  • Alignment - Enemy / Friend / etc.

This always leads to problems, as you have observed: sooner or later you'll want to create classes that should inherit from multiple subclasses of BaseCharacter (usually one from each aspect category), eg. Humanoid + Enemy. In this case either you use multiple inheritance (not recommended in C++ due to implementations issues), or you need to redesign your code.

Usually the best solution is to split up your object according to the aspects identified earlier. Eg. inside a BaseCharacter object, you could have two pointers, a CharacterForm* and a CharacterAlignment*. Then you can subclass CharacterForm (as HumanoidForm, AnimalForm, etc.) and CharacterAlignment (EnemyAlignment, FriendAlignment, etc.) independently, and combine them as needed.

Note that this approach really is the first step towards a component-based design, as Dan O suggested.

You need to do something similar every time you find yourself creating a subclass that is a subclass of its base in a different sense than previous subclasses (eg. after creating Humanoid and Animal you need Enemy -> this is clearly a different way of categorizing your BaseCharacters). The rule of thumb is that every class should only ever be subclasses according to one aspect.

imre
  • 1,667
  • 1
  • 14
  • 28
0

The issue to consider is whether a Humanoid instance needs to be converted to a BaseCharacter instance. You might find that it in fact does not. Do you actually need to iterate over the set of all Humanoid characters?

If so define:

Humanoid

BaseCharacter->PlayableCharacter (contains Humanoid)
            \---->Enemy---->EnemyHumanoid (contains Humanoid)

[ You might want to consider making Enemy abstract a defining a concrete EnemyNonhumanoid.]

An alternative is to define Humanoid as an interface, and provide a getHumanoid() method on PlayableCharacter and EnemyHumanoid, implemented as a handle in each case.

Keith
  • 6,756
  • 19
  • 23
0

How about this?

BaseCharacter

IBehaviour is interface

HumanoidBehaviour is implementation of IBehaviour

BaseCharacter contains IBehaviour

Enemy inherits BaseCharacter and contains HumanoidBehaviour

PlayableCharacter inherits BaseCharacter and contains HumanoidBehaviour

So logically we'll get: Enemy is-a BaseCharacter and has-a behavior HumanoidBehaviour.

Possibly Strategy design pattern will be helpful here Also there is funny book that has a nice example )

tim
  • 788
  • 4
  • 14