Monster Classes

Started by
8 comments, last by Roboguy 18 years, 4 months ago
How would I code a monster/item class in C++? I have read through several books and cannot figure out what the monster classes mean... can someone explain? I have the hit points/experiance part down.. but how do I get the game to recognize which monster is which?
Advertisement
Quite a broad question. How you would code such classes is largely dependent on you (either your design document, or your whims, or both, depending on your particular situation). First, ask yourself where exactly you need to distinguish between an Orc and a Paladin. The renderer doesn't care, it only cares about vertexes and textures (gross oversimplification). In your AI code, you'd probably want to distinguish between types.

Anyway, you probably want to keep a variable on each monster that identifies their "type". Adding new types may be cumbersome this way, but you may not want to use strings to identify the monsters (it might or might not make a performance difference, I have no benchmarks on this, and anyway you'd have to write some code and profile it first to get any meaningful results on that question).

Another (probably naive) way would be to derive each monster from an abstract base class. You don't really need to, and this makes adding new monsters even more difficult. I'd keep the monster class pretty generic instead, and add custom behavior through scripts or type identifiers or such.
Quote:Original post by Aviscowboy1
How would I code a monster/item class in C++? I have read through several books and cannot figure out what the monster classes mean... can someone explain?

I have the hit points/experiance part down.. but how do I get the game to recognize which monster is which?



class Monster{    //Code goes here};


...

Oh, you wanted more?

There are as many approaches as there are games. But a good place to start is to decide how your monsters are related. What are the similarities and differences? Lets take a hypothetical example of 5 monsters:

Name: Slime
Type: Squishy
Moves: Smack, Goo

Name: Trogdor
Type: Dragon
Moves: Punch, Fire

Name: Jelly Candle
Type: Squishy
Moves: Goo, Fire

Name: Kip
Type: Dragon
Moves: Smack, Fire

Name: Killer Bread
Type: Squishy
Moves: Mold

Now, how do you want to group these? I would suggest the following, by making a monster class, then deriving types from this class, then deriving monsters from these types.

class Monster
{
//Basic monster info, stats, HP, etc...
//Trait list could also be stored here, and initialized in the derived
//classes
};

class Dragon : public Monster
{
//Initializers and data that are specific to all Dragon types
};

class Squishy : public Monster
{
//Initializers and data that are specific to all Squishy types
};

class Trogdor : public Dragon
{
//Trogdor initializers, moves, AI, image/animations, etc...
};

class Slime : public Squishy
{
//Slime specific initializers, moves, AI, image/animations, etc...
};

The idea here is that many Dragons will share some of the same traits, such as resitance to fire and weakness to lances. However the Trogdor will have a specific set of stats and moves that are different from the Kip.

Of course, this is a contrived example, and your tree could get much more complex and look completely different if you so chose.

If you plan to have a large number of enemy types (let's say you're making Dragon Quest, only because that's what I'm playing right now) then you would want to find a way to read this class hierarchy from a set of external data, such as a text file (or a file exported from a tool) so that you can easily create and modify monsters.

I hope this helps, and I hope I understood your original post and didn't go off on a crazy tangent here :)

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

Quote:Original post by lightbringer
Anyway, you probably want to keep a variable on each monster that identifies their "type". Adding new types may be cumbersome this way, but you may not want to use strings to identify the monsters (it might or might not make a performance difference, I have no benchmarks on this, and anyway you'd have to write some code and profile it first to get any meaningful results on that question).

Another (probably naive) way would be to derive each monster from an abstract base class. You don't really need to, and this makes adding new monsters even more difficult. I'd keep the monster class pretty generic instead, and add custom behavior through scripts or type identifiers or such.


A properly designed monster hierarchy wouldn't be a naive approach, but a poorly designed one can be a disaster.

The problem with just having a "type" variable is that if you want anything to be particular to a certain monster you have to have a special case, usually implemented as a big ugly switch statement. There are nice organized ways to avoid these huge switches, but they require a level of software engineering that I'm guessing the poster does not have (since he is asking this question in the first place).

The hierarchy of monster types allows for a very organized solution, and it only makes things more difficult by requiring more thought to be put into each monster (by designing it's class). A coding style that makes you slow down and think really isn't a bad thing in my book...

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

I suppose I didn't think my reply through properly. You're right, just having one monster class is not such a good thing. I'm certainly not trying to advocate ten page long switch statements. I did think that anything more complicated would not benefit the OP, based on his post ^_^.

I'm really not sure if a class for every specific monster is needed though. I'd rather think of Monsters in terms of categories and behavior. A category (ie: Animal) would be derived from a common base class (in my case from Actor derived from Entity derived from VisNode derived from Node) and behavior added through interfaces (maybe something like Carnivore, Waterdweller, etc). Then we could have a DangerousLandAnimal (extends Animal, implements Carnivore, Walker, Hunter) which could be a T-rex or a Puma. That last level of specific behavior is provided in the instance (setSkin(), setName(), setAnimations(), etc). I think such an approach allows for more variation without the need for extensive programmer involvement. The engine doesn't need to know the difference between a T-rex and a Puma, short of their specific data members for purposes of AI and rendering (set in the instance). But as I'm still in the middle of thinking about my own system (working on rendering more than anything these days, it seems) I cannot really provide much meaningful input.

Edit: The case about adding class-specific properties would in this situation be also solved through interfaces. Even though Dragons might be reistant to fire, Troglodytes might also be so. Then, every Monster would implement an ElementalProperties interface, which would allow to transparently set and retrieve these on any Monster instance.

Edit2: @JBourrie: For the sake of discourse I've probably gone into more detail than necessary. You've only started to cursory describe a base-class-derived system, and there's of course much more to it, as you've said. But it's on topic and interesting (to me at least), so I suppose it won't hurt anyone. ^_^
Two words: data-driven.

Not to pick on the personal beliefs of anyone here, but class heirarchies hundreds of levels deep that differentiate SmallGoblinWithTwoWarts from LargeGoblinWithThreeWarts make me want to cry. While the idea of "Animal->Mammal-Dog" style hierarchies works well in the land of academia when trying to demonstrate principles such as inheritance and polymorphism, they tend to be quite painful in real life if you do not take great care in making them flexible, and even then there are just some things that you will find you cannot do (and often not until its very late).

As an off-the-cuff example (and prodding poor Joe's nice informative post... no offence meant Joe [smile]), I think a "monster" class is NOT a good candidate for a deep inheritance hierarchy... I just don't see the need to classify them more than one or two levels deep

I would tend to stop sub-classifying at a higher level than Joe's example, typically making all "monsters" on an equal level in the tree, and trying to keep the number of abstract sub-classifying levels to a minimum... and focus on making everything more driven by parameters (provided by serialized instances in a file, or passed by calling code, such as an AI agent governing the instance of the monster).

Please bear in mind this is totally of the top of my head, and I may recant at any time [smile]... and apologies, but I will be using C#-style syntax and including method bodies in the class definitions... so sue me
public abstract class GameObject{  // some root class which is often useful for providing functionality such as logging, diagnostics, persistence entry points, etc across many different kinds of game objects...}// class that represents any active unit in the game, whether enemy or friendly npcpublic abstract class Unit : GameObject{  private Avatar avatar; // reference to an object which contains and manages the visual representation of the unit... i.e. model and animation info, textures, etc  // any other common attributes that apply to most units  private Vector3D position;  private Vector3D orientation;  private Vector3D velocity;  private long hitPoints;  private long attackStrength;  // etc...  // set of virtual methods that are intended to be overridden by your concrete implementations  public virtual void Move(...){}  public virtual void Attack(...){}  // etc...}public class Goblin : Unit{  public override void Move(...)  {    // set the properties governing movement of this goblin according to any special rules you need...  }  public override void Attack(...)  {    // select and execute an attack according to current state of this goblin and/or any parameters passed in  }  // etc...}


Again, I apologise that this is such a rudimentary and incomplete example, and sorry if this makes my point no clearer...

Anyway, in short: inheritance is way overrated, and should be used judiciously, and data-driven designs (using composition) may very well make extending and tweaking your "monster" catalogue much easier down the track (especially if you can make it possible without having to recompile code

Some other ways to think about:

- parameterising every possible thing you can think of to do with monsters (i.e. giving them a metric buttload of fields to describe their state and control their behaviour), and have the Unit (or Monster, if you prefer) class methods try and perform actions according to the current values of all these properties. This method requires a LOT of fore-thought as to what your monsters will and will not be able to do, but really opens the game up for tweaking by non-programmers, as all that is required is fiddling of data values rather than deriving from a base class.

- write your Unit class to load and run scripts, meaning that again you wouldn't have to derive new classes and compile code in order to come up with new monster types and behaviours. This method would allow for some unseen requirements down the track, but some serious thought has to go into defining the code interface to allow this level of dynamicism.


Hope that was some food for thought anyways, and not horribly confusing [smile]...

Adam
Quote:Original post by Bad Monkey
Two words: data-driven.


Ahh...Washu's going to be so happy. My second link to his journal today.

Bad Monkey, Washu's all about "data-driven", and gives a bit more complete example of what you mentioned above using types of poisons that weapons in RPGs can inflict. I personally wouldn't hardcode the poison types, but, for purposes of showing how to refactor properly (although I have a few qualms about that particular link's so-called "refactoring"), it works well to complement what you posted above.


PS. At the moment, that journal link doesn't seem to work for me -- it may for you, but, just in case, his journal is here, and the post linked is about 8th from the top.
Things change.
"...but class heirarchies hundreds of levels deep that differentiate SmallGoblinWithTwoWarts from LargeGoblinWithThreeWarts make me want to cry"

I just want to point out that while I recommended using inheritance, I agree here, and it's actually what I mentioned above differentiating a "well designed" hierarchy from a "poorly designed" hierarchy. I would not suggest making the hierarchy too deep, but it can be a great way to organize and think out your monsters if you have a way to classify them.

I also suggested inheritance because I was leaving the concept open to each monster having alot of its own specific traits. If the only difference between your monsters are their stats and a list of moves, a hierarchy is a bad system to use. If each monster shares types of stats, but their actions are fundamentally different from each other, a hierarchy might be better. Its a bit unfortunate that the example I used was a dragon warrior clone, because that is a game where the monsters are very much the same.

Finally, I pointed out the hierarchy not even so much as to say "this is how it should be coded in C++". It was suggested as a way to organize them, and at the end of the post I attempted to make it clear (maybe not clear enough) that a data driven approach would be even better, and while it would have a similar structure it would replace the actual C++ classes with an abstract object created by data (like your previous suggestion).


Anyway, I don't think I was clear that I was suggesting a path and not an implementation. Once again, I'll reiterate : Inheritance and class hierarchies can be damn useful, IF they are well designed.

Dead horse, get up and go to the stable.

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

Quote:Original post by JBourrie... If each monster shares types of stats, but their actions are fundamentally different from each other, a hierarchy might be better.

Or, keep the hierarchy small and encapsulate the different actions in a seperate hierarchy. This way you can add more actions without changing the monster class at all, and you can even switch between different actions at run-time. Grouping sets of related actions together - attack, intimidate, run, speak, etc. - can help with this.
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Quote:Original post by Boku San
Quote:Original post by Bad Monkey
Two words: data-driven.


Ahh...Washu's going to be so happy. My second link to his journal today.

Bad Monkey, Washu's all about "data-driven", and gives a bit more complete example of what you mentioned above using types of poisons that weapons in RPGs can inflict. I personally wouldn't hardcode the poison types, but, for purposes of showing how to refactor properly (although I have a few qualms about that particular link's so-called "refactoring"), it works well to complement what you posted above.


PS. At the moment, that journal link doesn't seem to work for me -- it may for you, but, just in case, his journal is here, and the post linked is about 8th from the top.


The only difference between the two links you gave is that the last character in the first one doesn't appear to be ASCII. Is this the post you are talking about?

This topic is closed to new replies.

Advertisement