Reputation: 12136
I'm having doubts on how to proceed regarding the class structure of my game/simulation framework. Please note that I am doing this mostly for my own enjoyment, I know there are lots of good solutions already out there :)
The structure of the framework has, among other things, Scene
s that contain Actor
s. Both Scene
s and Actor
s can behave in many different ways WRT some aspects of how stuff works internally. I have implemented such behaviours using multiple inheritance, like so:
class ActorBase : public Object { ... };
class Actor : virtual public ActorBase { ... };
class CollisionBehavior : virtual public ActorBase { ... };
class BoundingBoxCollisionBehavior : public CollisionBehavior { ... };
class MyActor : public Actor, public BoundingBoxCollisionBehavior { ... };
This way, using virtual inheritance, I can separate the implementation of what the actor is from the implementation of what the actor does (or how it does it). Both the Actor
and ...Behavior
classes need access to the common base class ActorBase
for their implementation, but things are neatly compartmentalised and I can easily change the behaviour of an object by simply replacing the Behavior
class it inherits from.
This has its limitation though: since each behaviour follows its own internal rules, and an object can inherit from a single Behavior
(or else hell breaks loose because of ambiguous function calls etc), I can never have two actors with different behaviours interact with each other. Also, since I'm integrating the V8 Javascript Engine into the framework, I'd have to create wrapped child classes for each possible combination of Actor
(or Scene
) and their possible behaviours, and instancing these object in a mixed fashion from JS would result in a crash because, again, two object that behave differently cannot interact with each other (at least not for everything).
An alternative solution I thought would be to abandon this multiple inheritance structure in favour of something like this: each object that requires Behavior
s has many arrays, one for each Behavior
group (like collision behavior, movement behavior, etc), with a ...Behavior
object for each possible behavior. I can give an object a default behavior and, if it's ever needed (an interaction occurs with an object with a different default behavior), I can add such behavior to one (or both) objects so that they can interact with each other - there would need to be some sort of conversion policy, but thats not an issue. This would mean having a circular reference (the object instance references the behavior instance and the behavior instance references the object it's contained in) for the behavior to be able to access the base properties it needs to do its own thing. The Behavior
subclasses would also need to be friend
s with the Actor
class, to access protected and/or private members. I'm don't really like this road that much, and I think there might be a more elegant way to solve this problem, I just haven't thought of one yet.
Thoughts? :)
Bonus Question: should I spell Behavior or BehavioUr? Not a native speaker!
EDIT: Solved the bonus question - depends or where you feel you are when on the Internet! :)
EDIT 2: Changed from Behavior
to CollisionBehavior
for clarity (see answer below)
Upvotes: 0
Views: 213
Reputation: 299730
I am afraid you are mixing things here. As you noted there are many different kinds of behaviors 1:
And of course it makes no sense to have a WalkingBehavior
compare to a FightingBehavior
so it makes no sense to have them share a base class either.
As such, I think that you should just use Actors and provide methods to retrieve the behaviors on those:
class Actor {
public:
virtual WalkingBehavior const& walkingBehavior() const {
return DefaultWalkingBehavior;
}
virtual FightingBehavior const& fightingBehavior() const {
return DefaultFightingBehavior;
}
// ...
}; // class Actor
Where the default behavior is defined in such a way that it can interact with a more regular behavior of its class. Then, each specific interface such as FightingBehavior
should provide the necessary methods to understand this behavior.
Note that this method of encoding information is extremely versatile: you can mix behaviors that are determined at compile-time and behaviors that are determined at run-time (and may evolve at run-time) without any incidence on the clients.
1 Personally I just follow my built-in browser spell-checker.
Upvotes: 1