कई अद्वितीय हथियारों / मंत्र / शक्तियों के लिए कोड संरचना कैसे करें


22

मैं एक अनुभवहीन प्रोग्रामर हूं , जो FTL की नस में "रॉग्युलाइक-जैसे" गेम बना रहा है , पायथन (अभी तक केवल पाठ के साथ संबंधित नहीं है) के रूप में कोई PyGame नहीं है।

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

मेरी पहली वृत्ति में बेसिक वेपन वर्ग था, और उस वर्ग से विरासत में अलग-अलग हथियार थे। हालाँकि, यह मेरे लिए समस्याग्रस्त लगता है: या तो मुझे बेसिकवेपन वर्ग को इतना नंगे बनाना है कि यह मूल रूप से बेकार है (केवल सभी हथियारों में सामान्य रूप से नाम और प्रकार (पिस्तौल, कुल्हाड़ी, आदि) हैं, या मुझे हर भविष्यवाणी करनी होगी अद्वितीय प्रभाव मैं कभी भी आऊंगा और उस कोड को BasicWeapon में लाऊंगा।

उत्तरार्द्ध स्पष्ट रूप से असंभव है, लेकिन पूर्व अभी भी काम किया जा सकता है। हालाँकि, यह मुझे इस सवाल के साथ छोड़ देता है: मैं व्यक्तिगत हथियारों के लिए कोड कहां रखूं?

क्या मैं plasmarifle.py, rocklauncher.py, swarmofbees.py, etc आदि बनाऊं, और उन सभी को एक फ़ोल्डर में छोड़ दूं जहां से खेल उन्हें आयात कर सकता है?

या वहाँ एक डेटाबेस शैली फ़ाइल (शायद एक एक्सेल स्प्रेडशीट के रूप में कुछ के रूप में सरल) है कि किसी भी तरह से प्रत्येक हथियार के लिए अद्वितीय कोड होता है - eval / निष्पादित करने का सहारा लेने की आवश्यकता के बिना एक तरीका है?

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

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

क्या वहाँ एक अलग भाषा या उपकरण है जिसे मैं एक तरह के आधे-डेटा, आधे-कोड वाले चिमेरा के रूप में उपयोग कर सकता हूं? क्या मैं पूरी तरह से अच्छी प्रोग्रामिंग प्रैक्टिस कर रहा हूं?

तो मैं जवाब है कि नहीं कर रहे हैं की सराहना करेंगे OOP की मेरी समझ, सबसे अच्छे रूप में अधूरा है भी कंप्यूटर विज्ञान-y।

EDIT: वॉन हिल्ट्स ने नीचे अपनी पोस्ट में स्पष्ट किया है कि मैं अनिवार्य रूप से डेटा-संचालित प्रोग्रामिंग के बारे में बात कर रहा हूं। मेरे प्रश्न का सार यह है: मैं डेटा-संचालित डिज़ाइन को इस तरह से कैसे लागू कर सकता हूं कि डेटा में स्क्रिप्ट हो सकते हैं, नए हथियारों को सक्षम करने के लिए मुख्य कार्यक्रम कोड को बदलने के बिना नई चीजें करने में सक्षम हैं?



@ बाइट 56 संबंधित; लेकिन मुझे लगता है कि यह वही है जो ओपी बचने की कोशिश कर रहा है। मुझे लगता है कि वे अधिक डेटा संचालित दृष्टिकोण खोजने की कोशिश कर रहे हैं। यदि मैं गलत हूं तो मुझे सही करों।
वॉन हिल्ट्स

मैं मानता हूं कि वे अधिक डेटा उन्मुख दृष्टिकोण खोजने की कोशिश कर रहे हैं। विशेष रूप से, मुझे लगता है कि सवाल का जोश उत्तर की तरह: gamedev.stackexchange.com/a/17286/7191
MichaelHouse

आह, इस बारे में क्षमा करें। :) मुझे "स्वीकृत उत्तर" पढ़ने की बुरी आदत है।
वॉन हिल्ट्स

जवाबों:


17

जब तक आपका गेम पूरी तरह से अन-अपेक्षित और / या प्रक्रियात्मक हो या उत्पन्न न हो, तब तक आप लगभग निश्चित रूप से डेटा-संचालित दृष्टिकोण चाहते हैं ।

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

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

पूरक पढ़ने नीचे उपलब्ध है:


2
एक घटक आधारित प्रणाली की तरह, जहां घटक स्क्रिप्ट के माध्यम से पढ़े जाते हैं। इस तरह: gamedev.stackexchange.com/questions/33453/…
माइकलहाउस

2
और जब आप इस पर हों, तो उस डेटा का एक स्क्रिप्ट हिस्सा बनाएं ताकि नए हथियार बिना किसी मुख्य कोड परिवर्तन के नई चीजें कर सकें।
पैट्रिक ह्यूजेस

@ विगन हिल्ट्स: धन्यवाद, डेटा-चालित वही प्रतीत होता है जो मुझे सहज रूप से समझ में आ गया था कि मुझे जरूरत है। मैं थोड़ी देर के लिए प्रश्न को खुला छोड़ रहा हूं क्योंकि मुझे अभी भी उत्तर की आवश्यकता है, लेकिन शायद यह सबसे अच्छा जवाब होगा।
henrebotha

@ पैट्रिक ह्यूजेस: यह वही है जो मुझे चाहिए! मैं उसको कैसे करू? क्या आप मुझे एक साधारण उदाहरण या ट्यूटोरियल दिखा सकते हैं?
हेनरेबोथा

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

6

(मुझे टिप्पणी के बजाय जवाब प्रस्तुत करने के लिए खेद है, लेकिन मेरे पास अभी तक नहीं है।)

वॉन का जवाब बहुत अच्छा है, लेकिन मैं अपने दो सेंट जोड़ना चाहूंगा।

मुख्य कारणों में से एक आप XML या JSON का उपयोग करना चाहते हैं और रनटाइम में पार्स करना कोड को फिर से शुरू किए बिना नए मूल्यों के साथ बदलना और प्रयोग करना है। जैसा कि पायथन की व्याख्या है और, मेरी राय में, बहुत पठनीय, आपके पास एक शब्दकोश और सब कुछ के साथ एक फ़ाइल में कच्चा डेटा हो सकता है:

weapons = {
           'megaLazer' : {
                          'name' : "Mega Lazer XPTO"
                          'damage' : 100
                       },
           'ultraCannon' : {
                          'name' : "Ultra Awesome Cannon",
                          'damage' : 200
                       }
          }

इस तरह आप सिर्फ फ़ाइल / मॉड्यूल आयात करते हैं और इसे एक सामान्य शब्दकोश के रूप में उपयोग करते हैं।

यदि आप स्क्रिप्ट जोड़ना चाहते हैं, तो आप पायथन और प्रथम श्रेणी के कार्यों की गतिशील प्रकृति का उपयोग कर सकते हैं। आप ऐसा कुछ कर सकते हैं:

def special_shot():
    ...

weapons = { 'megalazer' : { ......
                            shoot_gun = special_shot
                          }
          }

हालांकि मेरा मानना ​​है कि यह डेटा संचालित डिजाइन के खिलाफ होगा। 100% डीडीडी होने के लिए आपके पास जानकारी (कोड) निर्दिष्ट करने वाले फ़ंक्शन और कोड होंगे जो विशिष्ट हथियार का उपयोग करेंगे। इस तरह आप DDD को नहीं तोड़ते, क्योंकि आप कार्यक्षमता के साथ डेटा नहीं मिलाते हैं।


धन्यवाद। बस एक साधारण कोड उदाहरण देखकर उसे क्लिक करने में मदद मिली।
हेनरेबोथा

1
अच्छा जवाब देने के लिए और टिप्पणी करने के लिए आपके पास पर्याप्त प्रतिनिधि है। ;) स्वागत हे।
ver

4

डेटा-चालित डिजाइन

मैंने इस प्रश्न को हाल ही में कोड समीक्षा के लिए प्रस्तुत किया ।

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

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

अन्य भाषाओं की तुलना में, जैसे कि C ++, जो संभवतः डेटा x इंजन इंटरैक्शन और सामान्य रूप से स्क्रिप्टिंग को संभालने के लिए स्क्रिप्टिंग भाषा (जैसे LUA) का उपयोग करेगी, और डेटा को संग्रहीत करने के लिए एक निश्चित डेटा प्रारूप (XML की तरह), पायथन वास्तव में हो सकता है यह सब अपने आप में (मानक को देखते हुए dictभी weakref, विशेष रूप से संसाधन लोडिंग और कैशिंग के लिए उत्तरार्द्ध)।

एक स्वतंत्र डेवलपर, हालांकि, इस लेख में सुझाए गए डेटा-संचालित दृष्टिकोण को अपने चरम पर नहीं ले जा सकता है :

डेटा-संचालित डिज़ाइन के बारे में मैं कितना हूँ? मुझे नहीं लगता कि गेम इंजन में गेम-विशिष्ट कोड की एक भी पंक्ति होनी चाहिए। एक नहीं। कोई हार्डकोड हथियार प्रकार नहीं। कोई हार्डकोड HUD लेआउट नहीं। कोई हार्डकोड इकाई एआई। नाडा। ज़िप बंद करें। कुछ भी नहीं।

हो सकता है, पायथन के साथ, कोई भी वस्तु-उन्मुख और डेटा-संचालित दृष्टिकोण दोनों के सर्वोत्तम से लाभ उठा सकता है, जिसका उद्देश्य उत्पादकता और विलुप्तता दोनों हो सकता है।

सरल नमूना प्रसंस्करण

कोड समीक्षा पर चर्चा किए गए विशिष्ट मामले में, एक शब्दकोश "स्थिर विशेषताओं" और व्याख्या किए जाने वाले तर्क दोनों को संग्रहीत करेगा - हथियार में कोई सशर्त व्यवहार होना चाहिए।

एक तलवार के नीचे के उदाहरण में कक्षा 'एंटीपलाडिन' के पात्रों के हाथों में कुछ क्षमताएं और आँकड़े होने चाहिए, और अन्य पात्रों द्वारा उपयोग किए जाने पर कम आँकड़ों के साथ कोई प्रभाव नहीं):

WEAPONS = {
    "bastard's sting": {
        # magic enhancement, weight, value, dmg, and other attributes would go here.
        "magic": 2,

        # Those lists would contain the name of effects the weapon provides by default.
        # They are empty because, in this example, the effects are only available in a
        # specific condition.    
        "on_turn_actions": [],
        "on_hit_actions": [],
        "on_equip": [
            {
                "type": "check",
                "condition": {
                    'object': 'owner',
                    'attribute': 'char_class',
                    'value': "antipaladin"
                },
                True: [
                    {
                        "type": "action",
                        "action": "add_to",
                        "args": {
                            "category": "on_hit",
                            "actions": ["unholy"]
                        }
                    },
                    {
                        "type": "action",
                        "action": "add_to",
                        "args": {
                            "category": "on_turn",
                            "actions": ["unholy aurea"]
                        }
                    },
                    {
                        "type": "action",
                        "action": "set_attribute",
                        "args": {
                            "field": "magic",
                            "value": 5
                        }
                    }
                ],
                False: [
                    {
                        "type": "action",
                        "action": "set_attribute",
                        "args": {
                            "field": "magic",
                            "value": 2
                        }
                    }
                ]
            }
        ],
        "on_unequip": [
            {
                "type": "action",
                "action": "remove_from",
                "args": {
                    "category": "on_hit",
                    "actions": ["unholy"]
                },
            },
            {
                "type": "action",
                "action": "remove_from",
                "args": {
                    "category": "on_turn",
                    "actions": ["unholy aurea"]
                },
            },
            {
                "type": "action",
                "action": "set_attribute",
                "args": ["magic", 2]
            }
        ]
    }
}

परीक्षण के प्रयोजनों के लिए, मैंने सरल Playerऔर Weaponकक्षाएं बनाईं: हथियार को रखने / लैस करने के लिए पहला (इस प्रकार इसकी सशर्त on_equip सेटिंग को कॉल करना) और बाद वाला एक एकल वर्ग के रूप में जो शब्दकोश से डेटा को पुनः प्राप्त करेगा, आइटम नाम के रूप में पारित Weaponआरंभीकरण के दौरान तर्क । वे उचित गेम क्लासेस डिज़ाइन को नहीं दर्शाते हैं, लेकिन फिर भी डेटा का परीक्षण करने के लिए उपयोगी हो सकते हैं:

class Player:
    """Represent the player character."""

    inventory = []

    def __init__(self, char_class):
        """For this example, we just store the class on the instance."""
        self.char_class = char_class

    def pick_up(self, item):
        """Pick an object, put in inventory, set its owner."""
        self.inventory.append(item)
        item.owner = self


class Weapon:
    """A type of item that can be equipped/used to attack."""

    equipped = False
    action_lists = {
        "on_hit": "on_hit_actions",
        "on_turn": "on_turn_actions",
    }

    def __init__(self, template):
        """Set the parameters based on a template."""
        self.__dict__.update(WEAPONS[template])

    def toggle_equip(self):
        """Set item status and call its equip/unequip functions."""
        if self.equipped:
            self.equipped = False
            actions = self.on_unequip
        else:
            self.equipped = True
            actions = self.on_equip

        for action in actions:
            if action['type'] == "check":
                self.check(action)
            elif action['type'] == "action":
                self.action(action)

    def check(self, dic):
        """Check a condition and call an action according to it."""
        obj = getattr(self, dic['condition']['object'])
        compared_att = getattr(obj, dic['condition']['attribute'])
        value = dic['condition']['value']
        result = compared_att == value

        self.action(*dic[result])

    def action(self, *dicts):
        """Perform action with args, both specified on dicts."""
        for dic in dicts:
            act = getattr(self, dic['action'])
            args = dic['args']
            if isinstance(args, list):
                act(*args)
            elif isinstance(args, dict):
                act(**args)

    def set_attribute(self, field, value):
        """Set the specified field with the given value."""
        setattr(self, field, value)

    def add_to(self, category, actions):
        """Add one or more actions to the category's list."""
        action_list = getattr(self, self.action_lists[category])

        for action in actions:
            if action not in action_list:
                action_list.append(action)

    def remove_from(self, category, actions):
        """Remove one or more actions from the category's list."""
        action_list = getattr(self, self.action_lists[category])

        for action in actions:
            if action in action_list:
                action_list.remove(action)

कुछ भविष्य के सुधार के साथ मुझे आशा है कि यह किसी दिन मुझे एक गतिशील क्राफ्टिंग प्रणाली की अनुमति देगा, पूरे भारत के बजाय हथियार घटकों को संसाधित करेगा।

परीक्षा

  1. चरित्र ए हथियार उठाता है, इसे लैस करें (हम इसके आंकड़े प्रिंट करते हैं), फिर इसे छोड़ दें;
  2. चरित्र बी एक ही हथियार उठाता है, इसे लैस करें (और हम इसके आंकड़े फिर से प्रिंट करते हैं कि वे कैसे भिन्न हैं)।

ऐशे ही:

def test():
    """A simple test.

    Item features should be printed differently for each player.
    """
    weapon = Weapon("bastard's sting")
    player1 = Player("bard")
    player1.pick_up(weapon)
    weapon.toggle_equip()
    print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
        weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))
    weapon.toggle_equip()

    player2 = Player("antipaladin")
    player2.pick_up(weapon)
    weapon.toggle_equip()
    print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
        weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))

if __name__ == '__main__':
    test()

इसे प्रिंट करना चाहिए:

एक बार के लिए

संवर्धन: 2, हिट प्रभाव: [], अन्य प्रभाव: []

एक एंटीपलाडिन के लिए

वृद्धि: 5, हिट प्रभाव: ['अपवित्र'], अन्य प्रभाव: ['अपवित्र aurea']

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