कई विशेषताओं के साथ हीरो के लिए ओओपी वास्तुकला


14

मैं एक साधारण ब्राउज़र टेक्स्ट आरपीजी शुरू करने वाला हूं, ऐसे पात्रों के साथ जो (निष्क्रिय) अन्य लोगों से लड़ सकते हैं। इसमें विभिन्न हथियारों के लिए अतिरिक्त दक्षता के साथ ताकत, निपुणता और इतने पर लगभग 10 कौशल की एक सूची शामिल है।

क्या इस चरित्र वर्ग को डिजाइन करने का एक बेहतर तरीका है, बस उन कौशल को कक्षा की विशेषता के रूप में रखना? यह आसान लगता है, लेकिन मैं अनिच्छुक हूं क्योंकि यह अनाड़ी है।

class Char(self):
    int strength
    int dexterity
    int agility
    ...
    int weaponless
    int dagger
    ...

1
आपको खेल लिखने के लिए इस पूरी तरह से मार्गदर्शिका की जांच करनी चाहिए और कुछ सामान्य वर्ग लिंक की
ड्रेगन

@dragons दिलचस्प लिंक के लिए धन्यवाद, लेकिन मुझे Charactorकक्षा डिजाइन करने के लिए कोई गहरी व्याख्या नहीं दिख रही है ?
स्वेन

1
इस डिजाइन के बारे में आपको वास्तव में "अनाड़ी" क्या लगता है?
थॉमस

जवाबों:


17

जब तक आप अपने सिस्टम को अपेक्षाकृत सरल रखते हैं, तब तक यह काम करना चाहिए। लेकिन जब आप अस्थायी कौशल संशोधक जैसी चीजों को जोड़ते हैं, तो आपको जल्द ही बहुत सारे डुप्लिकेट कोड दिखाई देंगे। आप अलग-अलग हथियारों के साथ अलग-अलग दक्षता के साथ समस्याओं में भी भाग लेंगे। क्योंकि प्रत्येक कौशल एक अलग चर है, इसलिए आपको प्रत्येक कौशल-प्रकार के लिए अलग-अलग कोड लिखना होगा जो मूल रूप से एक ही करता है (या कुछ बदसूरत प्रतिबिंब हैक का उपयोग करें - इस शर्त के तहत आपकी प्रोग्रामिंग भाषा उनका समर्थन करती है)।

इस कारण से मैं आपको एक साहचर्य-संरचना में कौशल और दक्षता दोनों को संग्रहीत करने की सलाह दूंगा, जो कौशल-स्थिरांक को मूल्यों के लिए मैप करता है। कैसे करें कि सुरुचिपूर्ण ढंग से प्रोग्रामिंग भाषा से प्रोग्रामिंग भाषा में भिन्नता है। जब आपकी भाषा इसका समर्थन करती है, तो स्थिरांक एक में होना चाहिएenum

आपको एक उदाहरण देने के लिए कि यह व्यवहार में कैसे काम करेगा, हमले के नुकसान की गणना करने के लिए आपका कोड तब कुछ इस तरह दिखाई देगा:

int damage = attacker.getSkill(STRENGTH) + 
             attacker.getProficiency(weapon.getProficiencyRequired()) -
             defender.getSkill(TOUGHNESS);

एक विधि बनाना जैसे ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंगgetSkill() के मूल सिद्धांत के खिलाफ जाता है ।
जेफिर

4

संबद्ध सरणियों का उपयोग क्यों नहीं किया जाता ?, इससे आसानी से विस्तारित होने का लाभ मिलता है (उदाहरण के लिए PHP का उपयोग करके)

$Stats["Strength"] = "8";
$Stats["Dexterity"] = "8";

हथियार जैसी चीजों के लिए, आप शायद कुछ आधार कक्षाएं बनाना चाहेंगे

हथियार -> मेलेवेपॉन, रंगेडवेपन

और फिर वहां से अपने हथियार बनाएं।

अंतिम परिणाम जिसके लिए मैं लक्ष्य बनाऊंगा वह एक ऐसा वर्ग है

class Character
{
    public $Stats;
    public $RightHand;
    public $LeftHand;
    public $Armor;
    public $Name;
    public $MaxHealth;
    public $CurrentHealth;

    public function __construct()
    {
        //Basic
        $this->Name = "Fred";
        $this->MaxHealth = "10";
        $this->CurrentHealth = "10";

        //Stats
        $this->Stats["Strength"] = 8;
        $this->Stats["Dexterity"] = 8;
        $this->Stats["Intellect"] = 8;
        $this->Stats["Constitution"] = 8;

        //Items
        $this->RightHand = NULL;
        $this->LeftHand  = NULL;
        $this->Armor = NULL;

    }
}

यदि आप वास्तव में चाहते थे तो आप एक सरणी में सब कुछ स्टोर कर सकते हैं।


यह बहुत ज्यादा @Philipp ने क्या कहा?
AturSams

1
@ फिलिप ने सुझाव दिया कि एनम का उपयोग, सरणियाँ एक और विकल्प हैं।
ग्रिमस्टन

यह वास्तव में कहता है: "... साहचर्य डेटा-संरचना जो कौशल-स्थिरांक को मूल्यों के लिए मैप करती है", शब्दकोश में स्थिरांक तार हो सकते हैं।
AturSams

3

मैं इस सवाल का जवाब देने की कोशिश करूँगा कि सबसे अधिक ओओपी तरीका (या कम से कम मुझे क्या लगता है कि यह होगा)। यह पूरी तरह से ओवरकिल हो सकता है, जो आँकड़ों के बारे में आपके द्वारा देखे गए प्रस्तावों पर निर्भर करता है।

आप एक SkillSet (या आँकड़े ) वर्ग की कल्पना कर सकते हैं (मैं इस उत्तर के लिए C- जैसे सिंटैक्स का उपयोग कर रहा हूँ):

class SkillSet {

    // Consider better data encapsulation
    int strength;
    int dexterity;
    int agility;

    public static SkillSet add(SkillSet stats) {
        strength += stats.strength;
        dexterity += stats.dexterity;
        agility += stats.agility;
    }

    public static SkillSet apply(SkillModifier modifier) {
        strength *= modifier.getStrengthModifier();
        dexterity *= modifier.getDexterityModifier();
        agility *= modifier.getAgilityModifier();

    }

}

फिर हीरो के पास स्किलसेट टाइप का इंट्रेंसिकस्टैट्स फील्ड होगा। एक हथियार में एक संशोधक कौशल भी हो सकता है।

public abstract class Hero implements SkillSet {

    SkillSet intrinsicStats;
    Weapon weapon;

    public SkillSet getFinalStats() {
        SkillSet finalStats;
        finalStats = intrinsicStats;
        finalStats.add(weapon.getStats());
        foreach(SkillModifier modifier : getEquipmentModifiers()) {
            finalStats.apply(modifier);
        }
        return finalStats;
    }

    protected abstract List<SkillModifier> getEquipmentModifiers();

}

यह निश्चित रूप से आपको विचार देने के लिए एक उदाहरण है। आप डेकोरेटर डिज़ाइन पैटर्न का उपयोग करने पर भी विचार कर सकते हैं, ताकि आँकड़ों पर संशोधक "फ़िल्टर" के रूप में काम करें जो एक के बाद एक लागू होते हैं ...


यह सी-लाइक कैसे है?
बोगलज़

@bogglez: मैं वैसे ही "C'ish" का उपयोग करने के लिए pseudocode को संदर्भित करता हूं जो शिथिल रूप से ब्रेस भाषा से मिलता-जुलता है, भले ही कक्षाएं शामिल हों। आपके पास एक बिंदु है, हालांकि: यह अधिक विशिष्ट सिंटैक्स की तरह दिखता है - यह एक मामूली बदलाव है या दो योग्य जावा होने से दूर है।
cHao

मुझे नहीं लगता कि मैं यहां बहुत सख्त हो रहा हूं। यह सिंटैक्स में केवल एक अंतर नहीं है, बल्कि शब्दार्थ में अंतर है। C में क्लास, टेम्प्लेट, प्रोटेक्ट-क्वालिफायर, मेथड्स, वर्चुअल फंक्शन्स आदि जैसी कोई चीज नहीं है, मैं सिर्फ इस कैजुअल अपशब्दों का शौकीन नहीं हूं।
bogglez

1
जैसा कि अन्य लोगों ने कहा, घुंघराले ब्रेस सिंटैक्स सी से आता है। जावा (या उस मामले के लिए सी #) का सिंटैक्स इस शैली से बहुत प्रेरित है।
पियरे अरलाउड

3

चीजों को करने का सबसे ओओपी तरीका शायद विरासत के साथ कुछ करना होगा। आपका बेस क्लास (या भाषा के आधार पर सुपर क्लास) व्यक्ति होगा, तो शायद बेस क्लास से खलनायक और नायक विरासत में मिले। उदाहरण के लिए, ट्रांसपोर्ट के अपने मोड के अलग होने के बाद आपकी ताकत आधारित हीरो और फ्लाइट आधारित हीरो की ब्रांच होगी। इसमें जोड़ा गया बोनस है कि आपके कंप्यूटर खिलाड़ियों का मानव खिलाड़ियों के समान आधार वर्ग हो सकता है और यह आपके जीवन को सरलता से सरल बना देगा।

विशेषताओं के बारे में दूसरी बात, और यह कम OOP विशिष्ट है एक सूची के रूप में आपके चरित्र विशेषताओं का प्रतिनिधित्व करना होगा ताकि आपको उन सभी को अपने कोड में स्पष्ट रूप से परिभाषित न करना पड़े। तो शायद आपके पास हथियारों के लिए एक सूची और भौतिक विशेषताओं के लिए एक सूची होगी। इन विशेषताओं के लिए किसी प्रकार का आधार वर्ग बनाएं ताकि वे बातचीत कर सकें, इसलिए प्रत्येक को क्षति, ऊर्जा लागत आदि के रूप में परिभाषित किया गया है, इसलिए जब दो व्यक्ति एक साथ आते हैं तो यह अपेक्षाकृत स्पष्ट है कि बातचीत कैसे होगी। आप प्रत्येक चरित्र की विशेषताओं की सूची के माध्यम से पुनरावृत्ति करेंगे और प्रत्येक इंटरैक्शन में संभावना की डिग्री के साथ एक को दूसरे को होने वाले नुकसान की गणना करेंगे।

एक सूची का उपयोग करने से आपको बहुत सारे कोड को फिर से लिखने से बचने में मदद मिलेगी क्योंकि एक विशेषता के साथ एक चरित्र जोड़ने के लिए जिसे आपने अभी तक नहीं सोचा था, आपको बस यह सुनिश्चित करना होगा कि इसमें एक इंटरैक्शन है जो आपके मौजूदा सिस्टम के साथ काम करता है।


4
मुझे लगता है कि बिंदु हीरो की कक्षा से आँकड़े को हटाने के लिए है। वंशानुक्रम आवश्यक रूप से सबसे अच्छा ओओपी समाधान नहीं है (मेरे जवाब में मैंने इसके बजाय रचना का उपयोग किया)।
पियरे अरलाउड

1
मैंने पिछली टिप्पणी दूसरी। यदि चरित्र C और B से गुण हैं (जो दोनों का आधार वर्ग है) तो क्या होगा? आप या तो डुप्लिकेट कोड या एकाधिक वंशानुक्रम के विषय में कुछ समस्याओं का सामना करते हैं । मैं इस मामले में विरासत पर रचना का पक्ष लूंगा।
कॉमफ्रिक

2
काफी उचित। मैं सिर्फ ओपी को कुछ विकल्प दे रहा था। ऐसा नहीं लगता है कि इस स्तर पर पत्थर में बहुत अधिक सेट है और मुझे उनके अनुभव के स्तर का पता नहीं है। यह संभव है कि शुरुआती से पूरी तरह से बहुरूपता को दूर करने के लिए एक शुरुआत की उम्मीद करना थोड़ा अधिक होगा, लेकिन शुरुआत के लिए विरासत काफी सरल है। मैं कोड को 'अनाड़ी' महसूस करने के मुद्दे को सुलझाने में मदद करने की कोशिश कर रहा था, जिसे मैं केवल यह मान सकता हूं कि उन क्षेत्रों को कठिन कोडित किया गया है जो उपयोग में आ सकते हैं या नहीं। इन प्रकार के अपरिभाषित मूल्यों के लिए एक सूची का उपयोग करना imho एक अच्छा विकल्प है।
जेनेरिकजैम

1
-1: "चीजों को करने का सबसे ओओपी तरीका शायद विरासत के साथ कुछ करना होगा।" वंशानुक्रम विशेष रूप से वस्तु-उन्मुख नहीं है।
सीन मिडलिचच

3

मैं डेटा प्रकार (जैसे मैं XML का उपयोग करता हूं) और स्टेट ऑब्जेक्ट से टाइप किए गए एक स्टेट प्रकार के मैनेजर की सलाह दूंगा, जो कि टाइप करने के लिए हैशटेबल के रूप में कैरेक्टर इनटैन्स में संग्रहीत एक मान और कुंजी के रूप में स्टेट टाइप यूनिक आईडी के साथ है।

संपादित करें: Psudo कोड

Class StatType
{
    int ID;
    string Name;

    public StatType(int _id, string _name)
    {
        ID = _id;
        Name = _name;
    }
}


Class StatTypeManager
{
    private static Hashtable statTypes;

    public static void Init()
    {
        statTypes = new Hashtable();

        StatType type;

        type = new StatType(0, "Strength");
        statTypes.add(type.ID, type);

        type = new StatType(1, "Dexterity");
        statTypes.add(type.ID, type);

        //etc

        //Recommended: Load your stat types from an external resource file, e.g. xml
    }

    public static StatType getType(int _id)
    {
        return (StatType)statTypes[_id];
    }
}

class Stat
{
    StatType Type;
    int Value;

    public Stat(StatType _type, int _value)
    {
        Type = _type;
        Value = _value;
    }
}

Class Char
{
    Hashtable Stats;

    public Char(Stats _stats)
    {
        Stats = _stats;
    }

    public int GetStatValue(int _id)
    {
        return ((Stat)Stats[_id]).Value;
    }
}

मुझे लगता है कि StatTypeअनावश्यक में वर्ग, बस एक कुंजी nameके StatTypeरूप में उपयोग करें । जैसा कि #Grimston ने किया था। साथ ही Stats
गियान्निस क्रिस्टोफ़ाकिस

मैं इस तरह के उदाहरणों में एक वर्ग का उपयोग करना पसंद करता हूं ताकि आप स्वतंत्र रूप से नाम को संशोधित कर सकें (जबकि आईडी स्थिर रहती है)। इस उदाहरण में ओवरकिल? शायद, लेकिन चूंकि इस तकनीक का उपयोग एक कार्यक्रम में कहीं और कुछ इसी तरह के लिए किया जा सकता है, मैं इसे इस तरह से करूँगा।
DFreeman

0

An मैं आपको एक उदाहरण देने की कोशिश करूँगा कि आप अपने शस्त्रागार और अपने शस्त्रागार को कैसे डिज़ाइन कर सकते हैं।

हमारा लक्ष्य संस्थाओं को खत्म करना है, इसलिए हथियार एक इंटरफ़ेस होना चाहिए।

interface Weapon {
    public int getDamage();
}

मान लें कि प्रत्येक खिलाड़ी के पास केवल एक हथियार हो सकता है, हम Strategy patternहथियारों को आसानी से बदलने के लिए उपयोग कर सकते हैं।

class Knife implements Weapon {
    private int damage = 10;
    @Override
    public int getDamage() {
        return this.damage;
    }
}

class Sword implements Weapon {
    private int damage = 40;
    @Override
    public int getDamage() {
        return this.damage;
    }
}

एक अन्य उपयोगी पैटर्न खिलाड़ी के निहत्थे होने की स्थिति में नल ऑब्जेक्ट पैटर्न होगा ।

class Weaponless implements Weapon {
    private int damage = 0;
    @Override
    public int getDamage() {
        return this.damage;
    }
}

शस्त्रागार के लिए, हम कई रक्षा उपकरण पहन सकते हैं।

// Defence classes,interfaces

interface Armor {
    public int defend();
}

class Defenseless implements Armor {

    @Override
    public int defend() {
        return 0;
    }
}

abstract class Armory implements Armor {

    private Armor armory;
    protected int defence;

    public Armory() {
        this(new Defenseless());
    }

    public Armory(Armor force) {
        this.armory = force;
    }

    @Override
    public int defend() {
        return this.armory.defend() + this.defence;
    }

}

// Defence implementations

class Helmet extends Armory {
    {
        this.defence = 30;
    }    
}

class Gloves extends Armory {
    {
        this.defence = 10;
    }    
}

class Boots extends Armory {
    {
        this.defence = 10;
    }    
}

डिकॉउलिंग के लिए, मैंने डिफेंडर के लिए एक इंटरफ़ेस बनाया।

interface Defender {
    int getDefended();
}

और Playerवर्ग।

class Player implements Defender {

    private String title;

    private int health = 100;
    private Weapon weapon = new Weaponless();
    private List<Armor> armory = new ArrayList<Armor>(){{ new Defenseless(); }};


    public Player(String name) {
        this.title = name;
    }

    public Player() {
        this("John Doe");
    }

    public String getName() {
        return this.title;
    }


    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }

    public void attack(Player enemy) {

        System.out.println(this.getName() + " attacked " + enemy.getName());

        int attack = enemy.getDefended() + enemy.getHealth()- this.weapon.getDamage();

        int health = Math.min(enemy.getHealth(),attack);

        System.out.println("After attack " + enemy.getName() + " health is " + health);

        enemy.setHealth(health);
    }

    public int getHealth() {
        return health;
    }

    private void setHealth(int health) {
        /* Check for die */
        this.health = health;
    }

    public void addArmory(Armor armor) {
        this.armory.add(armor);
    }


    @Override
    public int getDefended() {
        int defence = this.armory.stream().mapToInt(armor -> armor.defend()).sum();
        System.out.println(this.getName() + " defended , armory points are " + defence);
        return defence;
    }

}

चलो कुछ गेमप्ले जोड़ते हैं।

public class Game {
    public static void main(String[] args) {
        Player yannis = new Player("yannis");
        Player sven = new Player("sven");


        yannis.setWeapon(new Knife());
        sven.setWeapon(new Sword());


        sven.addArmory(new Helmet());
        sven.addArmory(new Boots());

        yannis.attack(sven);      
        sven.attack(yannis);      
    }
}

देखा!


2
जब आप अपने डिजाइन विकल्पों के पीछे अपने तर्क की व्याख्या करेंगे तो यह उत्तर अधिक सहायक होगा।
फिलीपींस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.