Klassen Design



  • Servus Community,

    schon wieder bin ich hier mit einer Frage zu meinem Design. Gerade bei einem RPG, finde ich, ist Design wichtig und man lernt etwas über Hierachie. Um mir das nicht zu verhauen, werde ich wohl öfters mit solchen Fragen kommen. Außerdem wollte ich es eventuell als Basis eines RPG veröffentlichen.

    Ich habe mittlerweile eine Equipment-Klasse welche ein Dictionary<EquipmentParts, Item> enthält. EquipmentParts ist ein enum, welches angibt, ob das Item für den Kopf, die Brust usw. ist.
    Außerdem habe ich eine abstrakte Klasse Weapon welche von der abstrakten Klasse Item erbt. Diese Weapon-Klasse wird nur um ein AttackDamage-Propertie erweitert.
    In meiner Character-Klasse habe ich eine Methode Attack:

    public bool Attack(IAttackable target)
    {
        target.ReceiveDamage(this.Damage);
        return true;
    }
    

    this.Damage ist der Basisschaden des Charakters. Wenn ich jetzt noch den Schaden meines Schwertes, welches im Equipment ist, hinzuzählen will, muss ich downcasten. Mein Schwert ist nämlich ein Item mit einem AttackDamage-Propertie. Da aber nicht jedes Item Schaden hat, kann ich folgendes nicht machen:

    public bool Attack(IAttackable target)
    {
        // geht nicht, da Item kein AttackDamage aufweißt.
        target.ReceiveDamage(this.Damage + this.Equipment[EquipmentParts.Weapon].AttackDamage);
        // geht mit downcast
        target.ReceiveDamage(this.Damage + ((Weapon)this.Equipment[EquipmentParts.Weapon]).AttackDamage;
        return true;
    }
    

    Ich bin nicht unbedingt ein Fan von Downcasts. Deswegen habe ich versucht eine allgemeine Lösung zu finden. Jedes Item hat eigentlich einen Wert. Ein Heiltrank heilt 50 leben, ein Schwert hat 25 Angriff, die Brust hat 30 Rüstung, die Schriftrolle lässt mich 10 Minuten lang schneller laufen usw. Bis auf ein paar Ausnahmen, z.B. ein Brief der kein Wert hat, welcher aber dann irgendeinen speziellen Wert zurückgeben könnte (-1).
    Deswegen dachte ich mir folgendes:

    //Item.cs
    public abstract class Item
    {
        public abstract int GetValue();
    }
    
    //Weapon.cs
    public abstract class Weapon
    {
        public int AttackDamage { get; private set; }
        public override int GetValue()
        {
            return this.AttackDamage;
        }
    }
    
    //Character.cs
    public bool Attack(IAttackable target)
    {
        target.ReceiveDamage(this.Damage + this.Equipment[EquipmentParts.Weapon].GetValue());
    }
    

    Mit dieser abstrakten GetValue-Methode könnte ich auch woanders dann z.B. Zugriff auf den Wert wieviel ein Heiltrank heilt haben.

    Noch eine 2. Variante, welche ich aber persönlich nicht gut finde.
    In meiner Equipment-Klasse ändere ich mein Dictionary zu einer Liste und entferne das Enum komplett, womit ich weiterhin durch alle Items gehen kann, um sie im Equipmentfenster anzeigen zu lassen und ich habe von jedem Item in der Equipment-Klasse eine eigene Variable. Quasi so etwas:

    public class Equipment
    {
        public List<Item> Items { get; private set; }
        public Weapon Weapon { get; private set; }
        public Chest Chest { get; private set; }
        //...
    }
    

    Und dann kann ich in der Attack-Methode das hier machen:

    public bool Attack(IAttackable target)
    {
        target.ReceiveDamage(this.Damage + this.Equipment.Weapon.AttackDamage);
        return true;
    }
    

    Aber wie ich schon sagte, finde ich das persönlich nicht all zu schön.
    Ich hoffe ihr habt verstanden worauf ich hinaus will und könnt mir ein paar Vorschläge geben.

    Mit freundlichen Grüßen,
    Freaky

    Edit: Hm, wenn ich mir jetzt so die 2. Variante anschaue, bin ich evtl. noch überzeugter von ihr als von der 1. Ich bin mal auf eure Vorschläge gespannt.



  • Also ich find die zweite Variante wesentlich schöner als den Hack über ein "getValue()"...


  • Mod

    variante 1 ist in gewisser weise der anfang von data driven design. heiltrank auf etwas angewendet -> +20 health, schwert auf etwas angewendet -> -20 health. sowas wie die schriftrolle ist schon ein spezialobjekt, aber sowas wie schwert, hut, kegel und heiltrank scheinen nur modifier zu sein fuer den status von objekten.

    ich glaube beide varianten haben etwas, ich wuerde vermutlich variante 1 bevorzugen (ohne die ganze hierarchy), weil ich dann items einfuegen koennte indem ich einfach eine neue resource hinzufuegen, ala

    ZweischwaenzigeKillerKatzenHydra schrieb:

    <?xml version="1.0" encoding="UTF-8"?>
    <GenericItem name="bla" Slot="belt" energy_absolut="-10" energy_shieldmul="-0.5" use_count="infinity" health="100" usage_delay="10s" />

    es ist selten der fall, dass ein programmierer bei einem rpg alle items per klassenhierarchy hardcoded, meistens duerften das game designer sein die in scripten sowas festlegen.

    klassen wuerde ich nur erweitern, wenn sie neu funktionalitaet bringen die nicht unbedingt jedem objekt der parent klasse zusteht. wenn es z.B. eine schriftrolle ist die den spielstand speichert, wuerde ich das als sonderobjekt machen. gold das man findet und einfach nur den score aufwertet auch ein spsezial item, aber waffen, ruestungen, traenke (sofern sie dich nicht in andere objekte verzaubern), wuerde ich als generic item anlegen.

    aber version 2 ist auch ok.


Anmelden zum Antworten