1
\$\begingroup\$

This question may not be helpful to some but I'll ask anyway. I've hit a bit of a object orientated design issue. Making a RPG and the user gets to choose their own class i.e warrior/rogue/etc.

Sample:

int main()
{
    CharacterP *cPntr = new CharacterP // creates base pointer
    cPntr->createClass(cPntr, "Warrior"); // creates a new Warrior object which cPntr is now
                                           //pointing to.


    cPntr->printCharacterName();  //will print the warriors print name function        

    return 0;
};

class CharacterP
{
    void createClass(CharacterP* &samp, string choice)
    {
        if (choice == "Warrior")
            samp = new Warrior;
         // other classes go here...... i.e Rogue, Mage
    }

    virtual void printCharacterName() //I don't want this function here
                                      //as this function will never be used, however it is
                                      //needed as the type of pointer for cPntr is CharacterP
                                      //which is the base of warrior.
    {
        std:: cout << "i don't want this function here, although it is required;
    }

};


class Warrior : public CharacterP
{
    virtual void printCharacterName()
    {
        std::cout << "you printed warrior";
    }
};

The main problem is the way I've designed this: If I want to use a Warrior function, I also have to define it into the base class CharacterP. When i start branching this out and adding more classes i.e Mage/rogue its going to be a maintenance nightmare as the base class would have a lot of functions that won't be used at all.

Questions:

Why does this happen?

Why does the base class also need to have the same function even though the derived function is virtual?

What is a more efficient way of doing what i am doing? (giving the user the ability to create any classes they want i.e Warrior,Warrior,Mage/Rogue,Mage,Mage) maybe a way to create a warrior object with its own pointer which can be repeated so 3 warrior objects are created and can be used at runtime.

\$\endgroup\$
  • \$\begingroup\$ I don't know if this is your actual code, but as it stands you have a memory leak. You first assign cPntr to a new character and then reassign this pointer to a new Warrior without deleting or keeping the first pointer. I suspect your createClass wants to be static so you can call it directly. However, you're probably better following the other answers and using constructors, but in case you don't you should be aware of this. \$\endgroup\$ – T. Kiley Feb 24 '15 at 12:48
  • \$\begingroup\$ You should never ever use strings to distinct different cases (unless it comes from somewhere else of course, i.e. a config-file, user-input, ...) use enums instead. \$\endgroup\$ – tkausl Feb 24 '15 at 13:33
  • \$\begingroup\$ this is not the actual source code. i just tried to replicate it as close as possible. i used structs to identify each class, but an enum sounds better. \$\endgroup\$ – user59293 Feb 24 '15 at 14:39
3
\$\begingroup\$

The questions you should be asking yourself is if it makes even sense to treat different character classes as different code classes.

You usually use code-classes when you have objects which behave differently in terms of programming. But in many games, this isn't actually the case. Fighter, Warrior, Knight and Barbarian all can use the same programming, but only have different stats. These stat templates are better represented by instances of one code-class CharacterClass where each instance carries the data about how this character-class differs from the others. Your PlayerCharacter class would then reference the CharacterClass which applies to the character.

You could hardcode the stats of each CharacterClass, but it might be better to read this information from external config files for better maintainability.

When you have more different character-classes with notable differences in game mechanics (warriors, mages, archers, clerics...) it might still be possible to put them all into one code-class and instead extract their character-class features into individual code-classes. Each CharacterClass might have a list of CharacterClassAbilitys which are available to characters of that character-class. Examples for such class abilities would be MeleeAttack, RangedAttack, MagicAttack, PickLock or CureLightWounds. When your game features a progression mechanic where the character-class abilities are unlocked progressively, the information which abilities are already unlocked would be stored in PlayerCharacter, not in CharacterClass, because multiple characters can share the same character-class.

You might notice that this system easily allows you to create hybrid character-classes like "Spell Sword" which combines some of the character-class abilities typical to mages with some of the character-class abilities typical to warriors.

\$\endgroup\$
  • \$\begingroup\$ I'm somewhat confused about "Your PlayerCharacter class would then reference the Characterclass which applies to the character" \$\endgroup\$ – user59293 Feb 24 '15 at 15:29
  • \$\begingroup\$ @tropicalmemes What confuses you about that sentence? \$\endgroup\$ – Philipp Feb 24 '15 at 15:35
0
\$\begingroup\$

There is nothing wrong with the approach of defining the methods in the base class. In other languages, we would use interfaces for this, however this does not work in C++.

You should make your base class abstract and define the methods as abstract as well. This way you do not need to implement them in the base class.

class CharacterP
{
   void PrintName() = 0;
   void Walk() = 0;
   void Die() = 0;
}

As an additional benefit, you just have to take a look at your base class and know the features of the derived class. Being abstract, the methods force you to re-implement them when creating a new 'class' class.

Edit: The keyword in this context is 'polymorphism'. Have a look at it!

\$\endgroup\$
0
\$\begingroup\$

I think an important question is whether your character IS A warrior-rpgclass, or HAS A warrior-rpgclass

with an IS A relation, you would make the Warrior class inherit from your CharacterP class.

with a HAS A relation, you would give the CharacterP class a property called "Class", which holds an object of the Warrior class (which could inherit from a base Class abstraction)

a good principle for OOP is to favor composition over inheritance, as it allows for easier maintainability down the line.

\$\endgroup\$
0
\$\begingroup\$

I am working on a similar problem (granted I am using a different language so I'll try to only focus on principle). I basically made just a function for each part of the player that could be different at any point in the game and gave it conditions that would just add to the player's stats (I.e. Shield could be none, paper, glass, wood, or metal. Each different type added a certain number of points to the player's defense. ). I also added the currently unused ability to enchant the shield or armor or whatever (this currently is just a point system that does nothing but I'm thinking about doing something like giving a shield attack points). The only problem with this is it makes for some messy if statements.

What you could do is make a function that reflects the player's species and one for their class. You might want to start with some base stats (jump_height = 1 strength = 3 etc.) and then have the species and classes modify them (for an elf add 2 to the jump_height). I'm not that great with graphics but I would expect them to work on the same principle.

Hope this helps!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy