Konsolenspiel mit verschiedenen Helden



  • Achso, geht das nicht anders? Weil das wollte ich ja umgehen.
    Wenn ich jetzt 50 Helden habe, sieht das ja nur noch unschön aus.
    Oder ist es ein allgemeiner Designfehler?



  • Was meinst du genau, bzw. was ist dann eigentlich dein Problem? Ich hab nur deinen ursprünglichen etwas umgebaut, damit es funktioniert.



  • Wenn ich nun nicht 2 Helden habe, sondern z.B. 100, kann ich dann 100 mal in Folge

    if(selectedHero == "x") hero = new x
    

    schreiben?



  • Ok, das war aus deiner ursprünglichen Frage nicht rauszulesen. Du brauchst so ein if-else Konstrukt natürlich nicht. Wo kommt der ausgewählte Held überhaupt her? Du könntest z.B. eine map Name->Instanz aufbauen, z.B. so:

    std::map<std::string, Hero*> heroes;
    heroes["Jim"] = new Jim();
    heroes["John"] = new John();
    

    Oder du machst eine Liste und nimmst das Element an der Position, die im Menü gewählt wurde. Dieselbe Liste verwendest du dann natürlich auch, um das Menü erst aufzubauen.



  • Ja genau, ich will so ein riesiges if-else-Konstrukt vermeiden.

    Für die Helden gebe ich einfach eine Liste auf der Konsole aus:

    1. John: Ist toll
    2. Jim: Ist auch toll
    3: Peter: Ist ganz toll

    Und je nachdem welche Nummer man wählt, der Held wird dann auch gewählt.

    Lade ich bei dieser map-Varianten nicht immer alle Helden gleichzeitig, obwohl ich nur einen brauche? Zwar kann ich dann einen spezifisch ansprechen, aber in der Map existieren doch initialisierte Objekte zu ALLEN Helden?

    Was mir aber gerade beim Rumspielen mit den Maps aufgefallen ist:
    Laut http://www.cplusplus.com/reference/map/map/operator[]/ kann ich mir einen bestimmten Eintrag aus der Map wie folgt holen:

    std::map<std::string, int> m;
    m["John"] = 1;
    m["Jim"] = 2;
    
    // ---
    
    std::cout << m["John"];
    

    Sollte, zumindest laut der Cpp-Referenz funktionieren. Mein Visual Studio aber findet das weniger knorke, weil es meint, dass der << Operator für sowas ungeeignet ist. Ich krieg den entsprechenden Wert wenn ich über die Methode data() rangehe (also m["John"].data()) - aber über diese Methode finde ich nichts, daher weiß ich nicht, ob es die richtige Wahl ist.



  • Wenn du nicht für alle Helder initialisierte Instanzen vorhalten willst, obwohl du nur einen brauchst (ist auch sinnvoll so), brauchst du irgendeine Art Factory. Das einfachste wär hier wieder so ein switch-case Konstrukt. Eine andere Möglichkeit wäre hier noch Factory Klassen zu schreiben, die deine Helden erzeugen. Könnte man z.B. auch mit einer Map und Type Erasure lösen. Da könntest du dir vielleicht boost::factory anschauen.

    Dein Compiler gibt eindeutige Fehlermeldungen aus. Wenn er was nicht mag, dann machst du was falsch. Das sollte schon gehen, was du geschrieben hast, schau dir die genaue Fehlermeldung an.



  • Danke für den Tipp, werd ich mir ansehen.

    Wegen der Fehlermeldung; der Quellcode (sollte, meiner Meinung nach, eigentlich passen):

    #include <iostream>
    #include <map>
    
    int main() {
        std::map<int, std::string> m;
    
        m[1] = "John";
        m[2] = "Foo";
    
        std::cout << m[2];
    }
    

    Aber die Fehlermeldung:

    error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'std::basic_string<_Elem,_Traits,_Ax>' (or there is no acceptable conversion)
    


  • #include <string>



  • Oh. Vielen Dank.



  • Nur mal so generell: Wie unterscheiden sich die Helden denn? Ich tippe mal auf: Anderes Model, andere Fertigkeiten.. ? Das sind aber nur andere Belegungen für die selben Attribute. Hier bietet sich Vererbung eigentlich nicht an.



  • Richtig.

    Ich vermute mal, die schönste Variante wäre die ganzen Heldenattribute in einer Datenbank / Datei zu speichern und so auszulesen? XML würde sich hierfür ja eignen:

    <?xml version="1.0"?>
    <heroes>
        <hero>
            <name>Jim</name>
            <hp>1000</hp>
            <basedmg>250</basedmg>
            <armor>500</armor>
            <ability1>9128</ability1>
            <ability2>1928</ability1>
        </hero>
    </heroes>
    

    Wobei ich die verschiedenen Abilities einfach über eine ID speichere. Diese finden sich dann in einer Map, welche aus den IDs und Pointern zu den entsprechenden Funktionen besteht, oder?



  • Du könntest die Attribute schon als Member deiner Heldenklasse halten. (Du würdest bei der Instanziierung dann die Werte aus deiner XML da reinkonstruieren.) Vielleicht ändern die sich ja wenn man irgendwelche Gegenstände einsammelt.



  • Nuki schrieb:

    Wobei ich die verschiedenen Abilities einfach über eine ID speichere. Diese finden sich dann in einer Map, welche aus den IDs und Pointern zu den entsprechenden Funktionen besteht, oder?

    Nein, find ich auf jeden Fall nicht gut. Schon gar nicht irgendwelche Zahlen als IDs. Ich hab mich auch schon gefragt, warum dir für deine Helden nicht eine Klasse reicht, die unterschiedlich intialisiert wird. Aber da du geschrieben hast, du willst das Verhalten teilweise gravierend abändern hab ich mir gedacht, wird schon seinen Sinn haben.
    Ist das Verhalten wirklich so unterschiedlich, oder läuft es auf denselben Algorithmus hinaus, den man durch verschiedene Parameter beeinflussen kann?
    Wenn du wirklich verschiedene Algorithmen bauen willst, würd ich vielleicht sowas wie Strategies einsetzen, und den Helden dann aus Strategie-Komponenten aufbauen. Die kannst du dann über einen Namen oder so in der XML angeben und dann wieder über eine Art Factory registrieren.



  • In dem Zusammenhang wäre vielleicht das Stichwort Dependency-Injection angebracht.



  • Mechanics schrieb:

    std::map<std::string, Hero*> heroes;
    heroes["Jim"] = new Jim();
    heroes["John"] = new John();
    

    Hundert Instanzen erstellen, wobei man sicher ist, dass man eh nur eine braucht? Eher nicht 😉

    Ich sehe aktuell und auch sonst keine Notwendigkeit, das über Ableitung zu machen. Das wäre sogar hinderlich. Mach es wie du schon selbst gemacht hast über eine separate Konfiguration, womit dein Held dann "gefüttert" wird (muss nicht zwingend XML sein). Gerade wenn du grad am testen/balancen bist und ein paar Fähigkeiten des Helden austauschen willst, seine Werte verändern willst, oder auch schnell mal einen neuen Helden basteln willst, willst du nicht ständig dein Programm neu kompilieren müssen. Einfach Datei umschreiben oder neu anlegen, im Programm den Helden laden, fertig.

    Da die Fähigkeiten eh wieder Objekte sein sollten, kannst du sie dann trotzdem individuell gestalten, ohne an deinem Helden dafür was ändern zu müssen

    -edit-

    das würde es an sich sogar vereinfachen, später einen Spielstand zu erstellen. Einfach eine solche Konfigurationsdatei im Savegame ablegen und beim späteren Laden deinen Helden damit wieder initialisieren


Anmelden zum Antworten