Enemy classes

Started by
2 comments, last by dmatter 16 years, 10 months ago
Hi everyone, I'm developing a 2D action-rpg, and was wondering how I would implement the hordes of enemies. I have a heirarchy of Characters, split into pCharacter and aCharacter, standing for Player Character & AI Character respectively. From aCharacter there are three subclasses, Fighter, Caster, and Hybrid. The Character class is virtual, and handles basic attributes & their functions (Damage calculation (if no override is needed), stats, things like this). The aCharacter class handles some of the AI's team tactics, passing them into the Figher, Caster, and Hybrid classes for the specific AI method to execute the decided enemies tactic. aCharacter also handles the random generation of enemies, just making the decision then passing it into the subclasses for the actual creation and levelling of the aCharacter. What I'm curious about, is whether for each enemy I need to create a specific class for it. IE A goblin would have its own class defining its type (fighter, hybrid, caster) with a possible override of the class's virtual levelling & AI, because every enemy will have its own special attack, which may vary widely between enemies. This class would also be able to take care of the enemy's specific spell list & personality (very agressive in its AI, defensive, long-range, etc) My question is whether writing a class for every enemy is necessary, or if there is a better way to do this. Character, aCharacter, Fighter/Hybrid/Caster classes would have many virtual functions (damage calculations, AI decisions) that would be overriden if a given enemy was to act a different way, so I think this is the best way, but if anyone has any suggestions, I'm very open to them. I also think that the individual class would be able to initialize all that enemy's specific sprites & sounds in the constructor of the class, so please account for where I would do this if you have a different idea. Thank you.
Advertisement
It's definitely best to write a class for each character. Abstraction is our friend.
I could recommend everything just being player objects and have an object in that player object for AI. Also, make it so that each different playclass uses the same player class.... make sense? Don't make a fighter class, mage class, etc. That's too complicated and isn't really what abstraction and polymorphism is all about.
Quote:Original post by xerodsm
It's definitely best to write a class for each character.

Really? I would say its definately not the best way. You rarely find there's a best-for-all design in reality.

Quote:Abstraction is our friend.

True, but abstraction is a tool, it's by no means the be all and end all of object oriented design. Nor is it always 'better' than any other paradigm that could be suitable here.

I think:
- Each class should have no more and no less than one responsibility
- You should favour aggregation over inheritance
- A data-driven design will allow easily customisable characters


What does this all mean then?

- One responsiblity per class
Every class must have a single purpose and clearly defined function-set and data-set that is implied by its name. At the moment you have your classes to handle characters, a single class is currently responsible for what it looks like, what it behavies like, and how it spawns into the world, all of this is complicatedly put in one class instead of split into several smaller, simpler ones.

- Aggregation over Inheritance
Aggregation is just 'composition' which is just what it sounds like, you compose functionality and data into a class by giving it instances of other classes which it holds on to.

Basically this builds on the last point, you have a class that requires lots of functionality and data but rather than put it all into one class you build several smaller classes that encapsulate a responsibility with its functions and data (like a Controller class for AI and a Sprite class for its image) and then put these into a larger class.

- Data-driven design
This means the data maintained within each class isnt hard-coded into it like a class with a specific skill/magic-set, rather the data comes from an externally loaded file or from the user. That way you dont need to make things like CAggresiveMage and CWimpyMage, you can just have a single Mage class and tell it whether its aggressive or wimpy or blue or red etc.



So you might like to have something like this:
typedef std::vector<Attack> attack_vector;typedef std::vector<Spell>  spell_vector;class Character : public SceneObject{public:    // CTor    Character(Sprite& sprite, Clothing& clothes, Controller& ai, attack_vector& attacks, spell_vector& spells)       : _view(sprite),         _clothes(clothes),         _controller(ai),         _attacks(attacks),         _spells(spells),         health(100),         strength(100) { }    //......private:    Sprite _view;           // What do I look like?    Clothing _clothes;      // What am I wearing? Threads? Armour?    Controller _controller; // How do I behave?    attack_vector _attacks;  // What attacks do I have?    spell_vector _spells;    // What spells do I have?    unsigned int health;    unsigned int strength;    //.......};void Game::createCharacters(/*..Parameters..*/){    Sprite goblinSprite("Goblin.bmp");    Sprite mageSprite("Mage.bmp");    Clothing clothes("GreenTop.txt");    Controller goblinAI;    goblinAI.setAggression(50);    goblinAI.setJumpHeight(2);    goblinAI.setIdleAnimation("GoblinIdleAnim.txt");    goblinAI.setIdleWalkPath("WalkInCiclesPath.txt");    Controller mageAI = goblinAI; // You might want a mage to behave a bit like a goblin but not quite...    mageAI.setAggression(20);    mageAI.setIdleAnimation("MageIdleAnim.txt");    attack_vector attacks;    attacks.reserve(5);    functionToPickFiveRandomAttacks(attacks);    spell_vector spells;    spells.reserve(20);    functionToPickTwentyRandomSpells(spells);    // Create a goblin    Character goblin(goblinSprite, clothes, goblinAI, attacks, spells);    // Create a mage who is similar to a goblin except for appearance and AI    Character mage(mageSprite, clothes, mageAI, attacks, spells);    // Add both new characters to the world    worldCharacters["AngryGoblin"] = goblin;    worldCharacters["GreenMage"] = mage;}


And there you go [smile]
See how there are now a mage and a goblin created using the same basic classes but that they behave and look differently.

Hope that was at the very least insightful


Edit: You could also [google] for the Model-View-Controller pattern to see a similar but (I think) slighty different way of doing a similar thing to what I gave above. Its a commonly used model for tackling this sort of thing.

[Edited by - dmatter on June 2, 2007 9:33:25 AM]

This topic is closed to new replies.

Advertisement