Textbasiertes RPG



  • Hey Leute,

    im Moment bin ich gerade dabei den Umgang mit der Qt-Bibliothek zu erlernen,
    dachte mir jedoch ich programmiere mir nebenbei mal ein textbasiertes Rollenspiel.
    Damit habe ich jetzt mal angefangen und habe mittlerweile das Hauptgerüst wie ich meine fertig, zu dem ist schon mal ein Level spielbar( wenn auch sehr klein und kurz ).
    Ich wollte mal euch fragen wie ihr das Game findet, was man verbessern könnte, wie ihr den Code findet und und und...
    Natürlich nur falls ihr es auch ausprobieren wollt;-)

    Eins im voraus, ich weiß dass die Story nicht so wirklich der Burner ist, mir gehts aber auch gar net so um die Story sondern ja ums Gameplay und hauptsächlich einfach darum, dem Umgang mit C++ zu verbessern und dabei etwas zu lernen.

    Hier mal der Link meines Projektes, es nennt sich Der Eiserne Drache=)

    http://www.fileuploadx.de/851878

    Vielen Dank schonmal für eure Mühe und Hilfe;-)



  • Also zunächst ein Lob dafür, dass du dich nicht dafür entschieden hast, ein neues WoW zu entwickeln, sondern zum Lernen eine Konsolenapplikation erstellt hast. Damit bist du nämlich um einiges erfolgreicher und schlussendlich auch motivierter.

    Dein Code scheint mir größtenteils ganz okay. Die Klasse Gegner zeigt, dass du den Sinn von Vererbung soweit verstanden hast. Die Bennenung deiner Funktionen sind aber insofern problematisch, da du sie nicht konsequent in einer Sprache bennenst, sondern auch Englisch mit einbringst. Für ein Projekt an dem man nur selber arbeitet ist das natürlich egal, aber für zukünftige Projekte muss man das evtl. berücksichtigen.

    Zur Abstraktion lässt sich noch sagen, dass du anstatt der Variablen int waffenItem[2] und int ruestungItem[2] und itsRuestung lieber eine eigene Klasse einführen solltest. Eventuell sogar noch eine Oberklasse für beide.

    SpielObjekt
     - RuestungsObjekt
      - RuestungSchild
      - RuestungKettenhemd
     - WaffenObjekt
      - WaffeSchwert
      - WaffeArmbrust
    

    Damit hast du dann später die Möglichkeit, dass ein Spieler ein Objekt vom Typ "SpielObjekt" finden kann. Dabei ist es dann egal, ob es eine Rüstung oder eine Waffe ist. Man könnte auch Zaubertränke usw. einführen.

    Man könnte diese Items dann auch den Gegnern zur Verfügung stellen. Du kannst dann die Stärke des Gegners aus der Konstellation der Waffen berechnen lassen (Er könnte ja auch mehrere haben, falls das mit der Waffe möglich ist - zwei Schwerter oder Messer wären ja drinne, während zwei Armbrüste quatsch sind).

    Damit sparst du dir viel Arbeit. Ich würde sogar so weit gehen, dass ich für das Inventar eine eigene Klasse habe.

    C++ Pseudo-Code:

    enum ObjektTyp
    {
     WAFFE,
     TRANK,
     ETC
    };
    
    class SpielObjekt
    {
     ObjektTyp objTyp;
    };
    
    class ObjektWaffe
    {
     int schaden;
     int geschwindigkeit;
    
     ObjektWaffe()
     {
      objTyp = WAFFE;
     }
    };
    
    enum TrankTyp
    {
     HEILTRANK,
     MANATRANK,
     UNSTERBLICHKEITSTRANK
    };
    
    class ObjektTrank
    {
     TrankTyp typ;
    
     ObjektTrank()
     {
      objTyp = TRANK;
     }
    };
    
    class Heiltrank : class ObjektTrank
    {
     int heilung;
    };
    
    class Manatrank: class ObjektTrank
    {
     int mana;
    };
    
    class Schwert : public ObjektWaffe
    {
     ObjektWaffe()
     {
      setzeSchaden(100);
      setzeGeschwindigkeit(10);
     }
    };
    
    class Inventar
    {
     Items items;
    
     void hinzufugen(SpielObjekt objekt)
     {
      items.push(objekt);
     }
    };
    
    class Gegner
    {
     // ...
    };
    
    class KriegerMitZweiSchwertern : public Gegner
    {
     Inventar inventar;
     Krieger()
     {
      inventar.push(Schwert());
      inventar.push(Schwert());
    
      inventar.push(Heiltrank());
     }
    
     void gibSchaden()
     {
      int schaden = 0;
    
      for (alle Items im Inventar)
      {
       if (aktuellesObjekt.typ() == WAFFE)
       {
        schaden += aktuellesObjekt.Schaden();
       }
      }
    
      return schaden;
     }
    };
    

    Das ist kein echtes C++ und soll nur eine Idee geben, was so möglich ist.

    Du solltest übrigens anstreben, dass du die Dialoge, Level usw. aus Textdateien liest, so kannst du dann ohne neues kompilieren an dem Projekt weiterarbeiten.

    Eventuell hast du auch Speicherleaks in deinem Quellcode. Für das new des Helden habe ich spontan kein delete gefunden. Oder auch hier:

    Gegner* feind = new ZauberGegner( "Erkakeles", 25, 0, 1, 4 );
        if( !kampfsystem( spieler, feind ) )
            return; // Und wer gibt nun feind frei? Das delete wird nicht mehr erreicht.
        delete feind;
    

    Musst du mal schauen.



  • Hey Dummie,
    erst mal vielen Dank für die schnelle Antwort, bzw das Feedback.
    Ja ist mir jetzt auch aufgefallen, dass ich ein paar Speicherlecks habe, da muss ich auf jeden Fall noch ran=)
    Das mit den Dialogen u.s.w in Textdateien werd ich mir wohl auch noch zu Herzen nehmen;-)

    Hab grad gesehen dass die Datei immerhin schon 10 mal geladen wurde,
    vll könnten die dies auch geladen haben, mir auch mal ein kurzes Feedback geben, damit ich den Code noch verbessern kann und vor allem was lerne;-)

    Danke schonmal=)

    Gruß freeG



  • Hm... Code ist für den Anfang ganz OK. Allerdings solltest du dir mal const-references anschauen, denn das fehlte mir ein wenig. Ansonsten würde ich bei Klassen wie "Held" vielleicht daran denken, das Zeug in eine Struktur zu packen. Achja; Kommentare!



  • Hm, nettes Projekt.
    Kommentare habe ich jetzt nicht vermisst, der Code ist ja größtenteils durch sinnvolle Namensgebung selbstdokumentiert und an vielen Stellen gibt es noch zusätzliche Kommentare.
    Aufgefallen ist mir, dass oft die Kombination cout << "..."; cin.get(); vorkommt.
    Wenn du das in eine Hilfsfunktion à la "printAndWait" auslagerst, hast du in dem Bereich gleich nur noch halb so viele Zeilen.

    Wenn du Gebrauch von Smartpointern machst (scoped_ptr, shared_ptr und wie sie alle heißen), dann kannst du Speicherlecks weitgehendst vermeiden.
    Zudem sollte man sicher immer überlegen, wem ein bestimmtes Objekt eigentlich gehört - derjenige sollte sich nach Möglichkeit auch um dessen Ableben kümmern.

    Edit: in main.cpp, funktionen.cpp und levels.cpp fehlt noch #include <cstdlib> für (s)rand, sonst kompiliert das bei mir nicht.
    Bei der Funktion kampfsystem bietet es sich an, sehr viele Codeteile in Funktionen auszulagern, wo es Sinn macht. Das würde die Übersichtlichkeit stark verbessern.
    Im Moment ist es ja wirklich so, dass die gesamte Kampflogik in dieser einen Funktion steckt.



  • Kóyaánasqatsi schrieb:

    Hm... Code ist für den Anfang ganz OK. Allerdings solltest du dir mal const-references anschauen, denn das fehlte mir ein wenig. Ansonsten würde ich bei Klassen wie "Held" vielleicht daran denken, das Zeug in eine Struktur zu packen. Achja; Kommentare!

    Das mit den const-references ist mir klar, werd ich auch einbauen.
    Aber was ich nicht verstehe, wieso sollte ich Held in de struct stecken?
    Meinst ja alle Klassendaten in ne struct und diese dann in die Klasse?

    Ich verstehe blos nicht was mir das bringt?

    Vielen Dank schonmal



  • @Athar:
    Ich weiß nicht, ob man einen Anfänger, der scheinbar noch nicht sicher mit new/delete-Kombinationen umgehen kann, gleich auf intelligente Zeiger loslassen sollte. Nur so ein Gedanke.



  • Cox schrieb:

    @Athar:
    Ich weiß nicht, ob man einen Anfänger, der scheinbar noch nicht sicher mit new/delete-Kombinationen umgehen kann, gleich auf intelligente Zeiger loslassen sollte. Nur so ein Gedanke.

    Eigentlich gerade dann, oder? So können Fehler trotzdem vermieden werden.
    Es sei denn, du meinst den Lerneffekt, der bei zu frühem Einsatz von solchen Hilfsmitteln verloren geht. Das mag wohl stimmen.



  • Kóyaánasqatsi schrieb:

    Hm... Code ist für den Anfang ganz OK. Allerdings solltest du dir mal const-references anschauen, denn das fehlte mir ein wenig. Ansonsten würde ich bei Klassen wie "Held" vielleicht daran denken, das Zeug in eine Struktur zu packen. Achja; Kommentare!

    Könntest du mir bitte erklären was mir das bringt?

    Danke schonma



  • Soooo, nun ist Version 0.2 fertig.
    Es gibt zwar nur ein neues Level also Level 2, jedoch geht ab jetzt alles schneller, denn habe nun einiges verändert bzw neue Sachen eingeführt.
    Man hat jetzt ein Inventar, es gibt viele Items, man kann auch leicht neue hinzufügen. Dadurch wurde auch das Kampfsystem leicht verändert.
    Es gibt auch noch einige kleine Neuigkeiten bzw Änderungen.

    Ihr könnt euch ja wenn ihr wollt auch Version0.2 laden und ausprobieren und mir weitere Tipps und Änderungs- bzw Besserungsvorschläge posten.

    Freue mich schon auf euer Feedback.

    Hier der Link:http://www.fileuploadx.de/972877

    Gruß freeG



  • Habe mir das Spiel mal kurz angesehen.

    Zuerst habe ich es mal ausprobiert, bevor ich den Sourcecode angeschaut habe :). Was mir aufgefallen ist: Vielleicht solltest du den Text nochmals durchlesen, da sind noch ein paar kleinere Fehler drin. Die Formulierung könnte manchmal auch noch schöner sein (+ einige Umlaute, die nicht richtig dargestellt werden). Ausserdem verwendest du manchmal Sie, manchmal Du und manchmal die dritte Person.
    Das sind aber alles Kleinigkeiten, die nichts mit der Programmierung zu tun haben.

    Zum Code: Du hast viele Klassen, bei denen du für jedes Element eine get und eine set Funktion hast und die ganze "Logik" ausserhalb der Klasse stattfindet. Es wäre oft besser, Methoden zu verwenden und nicht alle Variablen von aussen änderbar zu machen.
    Ein Beispiel:

    // Aus dem Kampfsystem, nach einem gewonnenen Kampf
     // getExp() und setEXP() überprüfen nichts sondern lesen und schreiben
     // die Variable direkt
    
    int y = spieler->getEXP() + x * 2;
    spieler->setEXP( y );
    spieler->checkLevel();
    return true;
    

    Hier ein Vorschlag, wie man das in der Klasse lösen könnte

    class Hero
    {
      private:
        int exp;
        int level;
        void checkLevel();
      public:
        // kein setExp und kein setLevel mehr
        int getExp(){return exp;}
        void increaseExp(int amount)
        {
           // Evt. auch noch if (amount <= 0) return;
           exp+=amount;
           checkLevel();
        }
    };
    

    Das hat den Vorteil, dass du weisst, dass level immer den richtigen Wert hat, passend zu der Erfahrung. Das ist der Vorteil davon, dass die Variablen privat sind. Du musst dir keine Gedanken über den Levelaufstieg mehr machen (ausser ein paar Gedanken, wie du die checkLevel() Funktion optimieren könntest 🤡 ).

    Du verwendest die Klassen im Moment eher wie structs einfach mit get/set statt direkt auf die Variablen zuzugreifen. Die Möglichkeiten und Vorteile von Klassen nützt du nicht wirklich.

    Anderes Beispiel: Es wäre nützlich, wenn die Held Klasse sich selbst in eine Datei schreiben könnte. Du hast so etwas Ähnliches probiert mit einer friend funktion. Aber wäre es nicht angenehmner, wenn du spieler->writeToFile(filename) schreiben könntest? So ähnlich wie hier:

    spieler->writeInventar( fout, spieler->itsInventar ); 
    //...
    void Held::writeInventar( ofstream& fout, Inventar itsInventar )
    {
        itsInventar.writeOutInventar( fout, itsInventar );
    }
    

    Aber wieso übergibst du hier ein Inventar? Du willst vermutlich eh immer nur das eigene Inventar schreiben, also mach das doch auch ;).

    Ich habe gesehen, dass du den Vorschlag mit printAndWait übernommen hast. Jetzt hast du eine Datei mit der Story, die fast nur aus printAndWait Zeilen besteht. Von da aus ist es kein grosser Schritt mehr, den Text aus einer Datei zu lesen :).


Anmelden zum Antworten