Konzeptfrage(Arrays, struct...?)



  • Hallo, ich habe da eine Konzeptfrage...

    ich habe ein kleines Spiel mit Charaktern(einfache Konsolenanwendung). Nun soll es auch Attacken geben, die benutzt werden können.

    Ich habe 15 Attacken. Jede Attacke ist etwa so aufgebaut:

    struct Attack    //liegt im Header
    {
        string name;    //Name der Attacke
        short index;    //Index
        short damage;   //Schaden
        short type;     //Typ - noch nicht so wichtig...
    };
    

    Ich muss jetzt in einer externen Datei alle Attacken und ihre Variablendefinitionen unterbringen. Auf die Variablen soll nur zugegriffen werden, sie werden nur ein mal initialisiert und danach nicht mehr verändert.

    Ich hab es mit struct und folgender externen Datei gelöst:

    Attack At1;
    {
        At1.name = "Schubser";
        At1.index = 1;
        At1.damage = 10;
    }
    
    Attack At1;
    {
        At1.name = "Schlagen";
        At1.index = 2;
        At1.damage = 50;
    }
    

    Wichtig noch: Die verschiedenen Attacken sollten sich durch ihren Index identifizieren können. D.h. wenn Charakter1 die Attacke 1 bekommt, soll auch gleichzeitig der Rest der Werte erhältlich sein. Also sowas wie:

    attackiere(att[1]) ; Fügt 10 Schaden hinzu
    attackiere(att[2]) ; Fügt 50 Schaden hinzu
    
    //...
    
    void attackiere(short index)
    {
        meinSchaden = index.damage
    }
    

    Wie würde man so etwas elegant lösen? Und bin ich mit struct auf der richtigen Spur? Ich benötige einfach nur Ideen, es braucht keiner fertigen Code zu posten...



  • ich würde den index an sich nicht mit in dem struct speichern - hat ja nix mit der attacke an sich zu tun, wie man sie auswählt!?

    des weiteren finde ich short nich gerade gut gewählt... (ich würde int nehmen, weil durch alignment etc eh 4byte verbraucht werden und man nix gekonnt hat - schneller ist es auch nicht - es sei denn, du programmierst für 16bit plattformen ^^)
    ich würd es eigtl irgendwie in die richtung lösen:

    namespace attack_type
    {
     enum T
     {
      normal,
      magic //usw
     }
    }
    
    struct Attack
    {
    private:
        std::string name; //evtl auch const, oder willst du das später ändern können? von 'schubser' in 'großer schubser' oder so...
        unsigned int damage;
        attack_type::T type;
    public:
        Attack(const std::string &_name, unsigned int _damage, attack_type::T _type) : name(_name), damage(_damage), type(_type) {}
    
        attack_type::T GetType() const {return type;}
        unsigned int GetDamage() const {return damage;}
        const std::string& GetName() const {return name;}
    };
    

    die werte würde ich in ne *.txt datei oder so speichern (möglichst nicht binär, da du dir so vieles verbaust bzw unnötig schwerer machst) - also solltest du noch die operatoren >> und << überladen

    speicherung würde ich ein std::set nehmen - also würdest du noch ein paar vergleichsoperatoren brauchen...

    wenns aber eh nur 15 attacken sind und die nicht gerade 1mal pro millisekunde durchsucht werden, würde es auch ein std::vector tun - weils bei 15 nicht darauf ankommt, ob die Suchdauer logarithmisch oder linear ist...

    falls du viele attacken hast, die gleich heißen, bräuchtest du doch wieder nen index oder so...
    allerdings würde ich dann nur nen const char* als name speichern, der nur auf den jeweiligen namen zeigt... in etwa so:

    const char*[] attacken_v = {"eins", "zwei", "drei", "vier"};
    const size_t attacken_c = sizeof(attacken_v) / sizeof(attacken_v[0]);
    
    const char* GetAttackNamePtr(const std::string &val)
    {
     for(size_t i(0); i != attacken_c; ++i)
     {
      if (std::string(attacken_v[i]) == val)
        return attacken_v;
     }
     throw std::invalid_argument ("GetAttackNamePtr");
    }
    

    Fällt aber vermutlich unter frühzeitige Optimierung, da das eigtl Design davon nicht wirklich betroffen wäre...
    Außerdem würde ich auch die Werte wieder in ner *.txt-Datei speichern und am Anfang auslesen - also hat man auch nicht mehr das hässliche C-Array sondern nen std::vector...
    Allg. geht das sehr viel schöner zu lösen, aber es sollen ja auch alles nur Ansätze sein ^^

    bb

    PS:

    void attackiere(const Attack& att)
    {
     unsigned int dmg = att.GetDamage();
    //...
    }
    

    aber du müsstest der Fkt vermutlich noch ein Ziel übergeben 😛



  • Eine kurze Frage: Was sind diese ":" nach den Funktionsdeklarationen?

    Attack(const std::string &_name, unsigned int _damage, attack_type::T _type)
        : name(_name), damage(_damage), type(_type) {}   //und diese Variablendeklarationen?
    
        attack_type::T GetType() const {return type;}
        unsigned int GetDamage() const {return damage;}
        const std::string& GetName() const {return name;}
    };
    

    Und auf was bezieht sich das const?

    unsigned int GetDamage() const {return damage;}
    const unsigned int GetDamage() {return damage;}   // Ist das nicht das Gleiche?
    


  • 1. Initialisierungsliste

    2. Nein:

    unsigned int GetDamage() const //diese funktion ist eine konstante member-fkt - sie darf das objekt nicht verändern - ob sie aufgerufen wird oder nicht (bzw wie oft), spielt keine rolle
    {
    //wenn du das hier auskommentierst, wirst du einen compiler-fehler bekommen:
    //  damage = 3;
    
      return damage;
    }
    
    const unsigned int GetDamage() {return damage;} //dieses const bezieht sich auf den rückgabe-typen - da der allerdings kopiert wird, ist das ziemlich egal, ob der const ist oder nicht ^^
    

    bb


Log in to reply