Animation architecture pattern
I'm writing a game project as a hobby, and am looking for some architecural advice about how best to organize my game objects and their various animations.
For example, I have a class called Wizard, and a class called Dragon. A Wizard has, at least, 10 different specific animations associated with it, depending on its state, and the same for Dragon.
So my question is this: is there a standard pattern for organizing these sorts of objects so that it is efficient and easy to extend (i.e. it should be easy to add new animations and new objects)?
开发者_JS百科I have various ideas about how to move forward on this, but I don't want to get this wrong since it is such an important part of the game architecture. It seems easy to get this working for a small game, but I am afraid of unmanageable complexity as it gets bigger.
I suggest having an interface such as "Character", which defines what all of these entities should be able to do. Then your Dragon and Wizard classes are just implementations.
Another route is to have a Base class from which you extend from and use this to control the "hierarchical sprawl" associated with large projects, by drawing out a hierarchy of your objects and identifying extended base classes.
No, there is absolutely no standard pattern.
My advice is to avoid the novice OO design pitfall of trying to find a single common "base class" interface for everything. Your Dragon is vastly different from your Wizard, and there's nothing to gain by attempting to merge the two interfaces, except cute code abstractions. Remember, inheritance is not a tool for code reuse.
The simplest approach is for each object to maintain its own internal state (as you have already described), and be able to draw that state. This is where an old school switch statement may be more clear than using polymorphism for the same effect. Example:
class Dragon {
enum State { asleep, flying, breathing_fire };
public:
void draw () { /* old-school switch */
switch (cur_state){
case asleep: draw_asleep ();
case flying: draw_flying ();
case breathing_fire: draw_breathing_fire();
}
}
private:
void draw_asleep ();
void draw_flying ();
void draw_breathing_fire();
};
Experienced C++ developers will gasp at the above code (as they should), because the switch statement is almost exactly what a polymorphic method call would accomplish -- runtime dispatch. (Or even more cryptic: a table of method addresses.) My personal opinion is that for this specific type of class, i.e., a single public interface encapsulating a state machine, the switch statement is more clear and maintainable because it makes the state machine jump explicit. I think it's also clear that I recognize this is generally bad C++ design.
The friction of this design is caused by the dividing line between the state machine and the single public interface. A sleeping dragon is clearly not the same as a flying dragon. In fact, it will have just about nothing in common. But the higher level design says that the two are the SAME dragon. What a mess! Do you create different Dragon objects for each state? No, because that would expose the state concept to the caller. Do you create different internal helper objects for each state and dispatch to those? Possibly, but it gets messy quickly. The above approach is a compromise between the two. Think of the switch statement as isolating the ugliness. The public interface is clean, as is the private interface. Only the switch statement contains the dirty mess. Consider this approach, and also consider other suggestions as well.
Finally, to draw your Wizard and your Dragon, a simple wrapper template or functor will suffice:
struct Draw {
template <class S>
void operator()(S& s) { s.draw (); }
};
The point is that you don't have to merge two different classes just because they both support the same logical operation.
精彩评论