Klassen als Variable an andere Klassen übergeben?



  • Halli Hallo liebe Gemeinde 🙂

    Ich lerne zurzeit recht fleissig c++ und bin momentan bei dem Thema Klassen.
    Um einfach zu üben, schreibe ich (wie so viele 😉 ) an einem kleinen Konsolen Rollenspiel.

    Soweit funktionieren die ersten Dinge schon ganz gut. Nun möchte ich aber der Klasse "Player" gerne bei den public variablen die Klasse "Weapon" mitgeben.

    Also z.b. Wenn man ein Schwert anlegt, dass dann auch bei dem Objekt "Spieler 1" als angelegte Waffe das Objekt "Schwert" vorzufinden ist.

    Die Idee hinter dem ganzen ist, dass ich den Schaden wie folgt berechnen möchte:

    Beim Angriff soll vom angelegten Waffenobjekt die Funktion Attack(); aufgerufen werden, in welcher ich den schaden der Waffe "auswürfel" und ggf. noch extra effekte hinzufügen kann. (Also Schwerter verursachen schwere Wunden, Äxte haben eine Chance die Rüstung zu verringern, sowas in der Art)

    Hier mal ein paar codeschnippsel:

    // Player.h
    
    class Player
    {
        private:
        string name;
        int level;
        int hp;
        int maxhp;
        int strength;
        int dexterity;
        int intelligence;
        int wisdom;
        int magic;
        int defense;
        int exp;
        int maxexp;
    
        public:
        Player();
        ~Player();
        void ShowInfo();
        void SetName(string newname);
        void CheckLevel();
        string GetName();
        int GetHp();
        int GetMaxhp();
        int GetStrength();
        int GetDexterity();
        int GetIntelligence();
        int GetWisdom();
        int GetMagic();
        int GetExp();
        int GetLevel();
        int GetDefense();
        void SetStrenght(int newstr);
        void SetDexterity(int newdex);
        void SetIntelligence(int newint);
        void SetWisdom(int newwis);
        void SetMagic(int newmag);
        void SetDefense(int newdef);
    };
    
    //Player.cpp (ich weiss, dass noch nicht alle Funktionen eingebaut sind, mache //ich noch, möchte aber zuerst das Waffenproblem lösen ;) )
    
    #include "Library.h" //Inkludiert meine ganzen header
    
    Player::Player() //default constructor
    {
        SetName("Held");
        strength = 0;
        intelligence = 0;
        dexterity = 0;
        wisdom = 0;
        magic = 0;
        defense = 0;
        maxhp = 20;
        hp = 20;
        level = 1;
        exp = 0;
        maxexp = 250;
    }
    
    Player::~Player()
    {
    }
    
    void Player::ShowInfo()
    {
        cout << "lvl: "<< level << endl;
        cout << "Hp : "<< hp << "/" << maxhp << endl;
        cout << "str: "<< strength << endl;
        cout << "dex: "<< dexterity << endl;
        cout << "int: "<< intelligence << endl;
        cout << "mag: "<< magic << endl;
        cout << "wis: "<< wisdom << endl;
        cout << "def: "<< defense << endl;
        cout << "exp: "<< exp << "/" << maxexp << endl;
    }
    
    void Player::SetName(string newname)
    {
        name = newname;
    }
    
    string Player::GetName()
    {
        return name;
    }
    
    int Player::GetHp()
    {
        return hp;
    }
    
    int Player::GetMaxhp()
    {
        return maxhp;
    }
    
    int Player::GetStrength()
    {
        return strength;
    }
    
    int Player::GetDexterity()
    {
        return dexterity;
    }
    
    int Player::GetWisdom()
    {
        return wisdom;
    }
    
    int Player::GetMagic()
    {
        return magic;
    }
    
    int Player::GetExp()
    {
        return exp;
    }
    
    int Player::GetDefense()
    {
        return defense;
    }
    
    void Player::SetDefense(int newdef)
    {
        defense = defense + newdef;
    }
    
    void Player::SetDexterity (int newdex)
    {
        dexterity = dexterity + newdex;
    }
    
    void Player::SetIntelligence(int newint)
    {
        intelligence = intelligence + newint;
    }
    
    void Player::SetMagic(int newmag)
    {
        magic = magic + newmag;
    }
    
    void Player::SetStrenght(int newstr)
    {
        strength = strength + newstr;
    }
    
    void Player::SetWisdom(int newwis)
    {
        wisdom = wisdom + newwis;
    }
    
    //Weapons.h
    
    #ifndef WEAPONS_H_INCLUDED
    #define WEAPONS_H_INCLUDED
    
    #endif // WEAPONS_H_INCLUDED
    
    class Weapon
    {
        private:
        string name;
        int min_damage;
        int max_damage;
        int value;
    
        public:
        Weapon();
        ~Weapon();
        int Attack(int str);
        void SetName(string newname);
        void SetDamage(int min, int max);
        void SetValue(int newvalue);
    };
    
    //Weapons.cpp
    
    Weapon::Weapon() //default constructor
    {
        name = "Waffe";
        min_damage = 1;
        max_damage = 2;
        value = 5;
    }
    
    Weapon::~Weapon()
    {
    
    }
    
    int Weapon::Attack(int str)
    {
        srand((unsigned)time(NULL));
        int dice = max_damage - min_damage; // Würfel erstellen
        int dmg = (rand()%dice) +1; // Würfeln
        dmg = dmg + str; // Erwürfelter Schaden + Stärke
        return dmg;
    }
    
    void Weapon::SetName(string newname)
    {
        name = newname;
    }
    
    void Weapon::SetDamage(int min, int max)
    {
        min_damage = min;
        max_damage = max;
    
        if(min_damage > max_damage) //Wenn der min_damage grösser als der max dmg ist, werden die Werte getauscht.
        {
            int temp = min_damage;
            max_damage = min_damage;
            min_damage = temp;
        }
    }
    
    void Weapon::SetValue(int newvalue)
    {
        value = newvalue;
    }
    
    //Weaponlist.cpp
    
    #include "Library.h"
    
    Weapon Bronzeschwert;
    Bronzeschwert.SetName("Bronzeschwert");
    Bronzeschwert.SetDamage(4, 12);
    Bronzeschwert.SetValue(20);
    

    Sooo, ist en bissl viel aber ich denke mal ist immer hilfreicher wenn man mehr postet statt weniger 😉

    Also könnt ihr mir da irgendwie helfen? Und wenn ihr noch Anmerkungen und Verbesserungsvorschläge zum Programmierstil etc. habt immer gerne her damit 🙂

    Im Voraus schonmal danke.



  • Anmerkungen:
    - Setter heißen normalerweise, dass ein Attribut auf den entsprechenden Wer gesetzt wird. Du addierst aber einen Wert hinzu, das ist irreführend. Ich würde daher die Getter umbenennen in AddWisdom etc.
    - Schau mal in deinem Buch nach "Initialisierugsliste", dann musst du im Konstruktor die Werte nicht überschreiben sondern initialisierst sie gleich richtig.
    - Schau mal nach Referenzen, du übergibst den String in der SetName-Methode per value, mit der Referenz sparst du dir eine Kopie.

    Zum Thema: Objekte übergibt man an Methoden genau so wie du das bisher gemacht hast per value oder eben per Referenz. Ob die Objekte jetzt einen simplen eingebauten Typ haben (int), oder einen Klassentyp aus der Standardbibliothek (std::string) oder einen selbst definierten Klassentyp (Weapon) macht da keinen Unterschied.



  • Godclass ...
    Einige deiner Setter sind unnötig. Warum sollte man den Namen des Spielers öfters umbennen können? Sowas kommt in den Konstruktor. Genau wie bei deiner Waffenklasse, da ändert sich der Name nicht und der Angriff der Waffe bleibt auch gleich, er wird später höchstens modifiziert.



  • pumuckl schrieb:

    Anmerkungen:
    - Setter heißen normalerweise, dass ein Attribut auf den entsprechenden Wer gesetzt wird. Du addierst aber einen Wert hinzu, das ist irreführend. Ich würde daher die Getter umbenennen in AddWisdom etc.

    Stimmt, da war ich zu voreilig, danke für den Hinweis, werde ich ändern 😉

    pumuckl schrieb:

    - Schau mal in deinem Buch nach "Initialisierugsliste", dann musst du im Konstruktor die Werte nicht überschreiben sondern initialisierst sie gleich richtig.

    Auch das werde ich tun, danke 🙂

    pumuckl schrieb:

    - Schau mal nach Referenzen, du übergibst den String in der SetName-Methode per value, mit der Referenz sparst du dir eine Kopie.

    Okay auch hier werde ich noch etwas lesen müssen.

    pumuckl schrieb:

    Zum Thema: Objekte übergibt man an Methoden genau so wie du das bisher gemacht hast per value oder eben per Referenz. Ob die Objekte jetzt einen simplen eingebauten Typ haben (int), oder einen Klassentyp aus der Standardbibliothek (std::string) oder einen selbst definierten Klassentyp (Weapon) macht da keinen Unterschied.

    Dieser Satz verwirrt mich ein kleines bisschen.

    Meinst du das so, dass z.b. Bei der Player class dann in der Attack Methode sowas steht wie:

    int attack()
    {
       schaden = Schwert.attack();
      // restlicher code
    }
    

    und wenn ja, ist für mich trotzdem noch die Frage wie ich im Spielerobjekt festlegen kann, dass der Spieler jetzt ein Schwert trägt.

    Oder kann ich da irgendwie einfach einen Pointer anlegen a la

    *angelegtewaffe = Schwert;
    
    und dann die Funktion quasi abändern in
    int attack()
    {
    Schaden = &angelegtewaffe.attack();
    }
    

    FreakY<3Cpp schrieb:

    Godclass ...
    Einige deiner Setter sind unnötig. Warum sollte man den Namen des Spielers öfters umbennen können? Sowas kommt in den Konstruktor. Genau wie bei deiner Waffenklasse, da ändert sich der Name nicht und der Angriff der Waffe bleibt auch gleich, er wird später höchstens modifiziert.

    Naja ich dachte, dass man später vielleicht noch Titel tragen kann, aber beim genaueren Nachdenken könnte ich natürlich auch einfach eine zweite Variable dafür anlegen 😉

    Bei den Waffen wollte ich dann halt noch sowas hinzufügen wie aus diablo 2

    "Schwert der Flammen" oder so, wenn man es. z.b. verzaubert hat und dementsprechend sollte sich auch der Schaden ändern, daher diese Funktionen 🙂



  • Lustig das alle irgendwie ein RPG Kampfsystem implementieren wollen 😉

    //Item.h
    
    #ifndef ITEM_H_INCLUDED
    #define ITEM_H_INCLUDED
    
    class Item
    {
        protected:
        string name;
    
        public:
        Item(string name = "", weight = "0") : name(name) /*, weight(weight), durability(100) */ {};
    
        string getName() const { return name;}
    }; 
    #endif // ITEM_H_INCLUDED
    
    //Weapons.h
    
    #ifndef WEAPONS_H_INCLUDED
    #define WEAPONS_H_INCLUDED
    
    class Weapon : public Item
    {
        protected:
        int min_damage;   // -10 erlaubt, ich schlag dich und heil dich dabei ?
        int max_damage;
        int bonus_damage;
    
        public:
        Weapon(string name="Waffe", int minDam=1, int maxDam=2);
    
        int attack();
        void setBonus(const int bonus);
    }; 
    #endif // WEAPONS_H_INCLUDED
    
    class MagicWeaponEffect: public Weapon
    {
        public:
            MagicWeaponEffect(string name="its magic", int minDam=5, int maxDam=5) : Weapon(name, minDam, maxDam) {};
    }
    
    Weapon::Weapon(string name, int minDam, int maxDam) : Item(name), min_damage(minDam), max_damage(maxDam), bonus_damage(0) 
    { 
        if (min_damage < max_damage) std::swap(min_damage, max_damage); // algorithm.h
    
        srand((unsigned)time(NULL));  
    }
    
    int Weapon::attack()
    {
    ;
        int dice = max_damage - min_damage; // Würfel erstellen
        int dam = (rand()%dice) +1; // Würfeln
        dam += bonus_damage; 
    
        if (dam>0) return dam;
    
        return 0;
    }
    
    void Weapon::setBonus(const int bonus)
    {
        bonus_damage = bonus;
    }
    

    Dann kannst du (falls du mehrere Waffen mitführst) folgendes machen:

    int Player::volleAttacke(const Weapon &w, const MagicWeaponEffect &m) const
    {
       return GetStrength() + w.attack() + m.attack();
    }
    

    irgendwo dann:

    enemy.reduceHPby(player.volleAttacke(SchwertA, Flammenschlag));  // oder so ...
    

    Natürlich könnstest du auch einfach eine der Waffen als aktiv (in Hand) markieren und automatisch auf den Schaden der aktiven
    Waffe zurückgreifen, ... oder oder oder



  • Fkscorpion schrieb:

    ...

    Und zu guter letzt verwundert mich auch, wie viele doch noch mit scheinbar alten Headern oder "using namespace" in Headern arbeiten.

    // <-- C++ Standard Header benutzen wie <string>
    // <-- Kein using namespace in Headern (Hat mehr Nach- als Vorteile)
    
    class Player
    {
        private:
            std::string name; // Im Header besser mit Namensraum
    ...
    };
    

    using namespace sollte ausschließlich in die Sourcedateien (wenn überhaupt) und dort hinter allen includes.



  • Ich sehe es ganauso wie 'asc'.

    Als Nachtrag möchte ich noch hinzufügen, daß man auch folgende Variante verwenden kann (um nicht explizit immer 'std::' im gesamten Header verwenden zu müssen):

    // eigenen Namensbereich benutzen
    namespace RPG
    {
        using std::string; // nur innerhalb dieses Namensbereiches gültig
    
        class Player
        {
        private:
            string name;
        ...
        };
    }
    

    (steht z.B. auch im Stroustrup "Die C++ Programmiersprache" unter Kapitel 8.2.2 "Using-Deklarationen" bzw. 8.2.3 "Using-Direktiven")



  • Okay, euch allen schonmal ein grosses Dankeschön für eure Hilfsbereitschaft,
    vor allem padreigh, der ja schon "fast" alles fertig geschrieben hat 😉

    Ich hab mittlerweile die namespaces rausgenommen und mich auch schon prima drann gewöhnt einfach ein std:: vorher zu schreiben. Ich hatte die namespaces benutzt weil es in den Buch Beispielen auch so war und bequemer zu schreiben für cout etc. Mittlerweile habe ich aber wieder etwas mehr gelernt und verstehe nun auch die Syntax hinter std:: 🙂

    Ich werd wohl das ganze Projekt einfach nochmal von vorne anfangen und mit versuchen mit den gelernten Sachen umzusetzen, bevor ich jetzt ein codegewurschtel hoch10 habe.

    Sollte es nennenswerte Ergebnisse geben, werde ich Sie hier posten.

    Ansonsten: Danke nochmals und bis zur nächsten Frage 😉


Log in to reply