लचीली बफ़ / डेबफ़ प्रणाली को लागू करने का एक तरीका क्या है?


66

अवलोकन:

आरपीजी जैसे आंकड़ों के साथ खेल के बहुत सारे चरित्र "बफ़्स" के लिए अनुमति देते हैं, सरल "डील 25% अतिरिक्त क्षति" से लेकर और अधिक जटिल चीजों जैसे "हिट होने पर हमलावरों को 15 नुकसान वापस करें"।

प्रत्येक प्रकार की बफ की बारीकियां वास्तव में प्रासंगिक नहीं हैं। मैं मनमाने शौकीनों को संभालने के लिए (संभवतः वस्तु-उन्मुख) तरीके की तलाश कर रहा हूं।

विवरण:

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

क्या यह समाधान समझ में आता है? मैं निश्चित रूप से दर्जनों घटना प्रकारों को आवश्यक रूप से देख सकता हूं, ऐसा महसूस होता है कि प्रत्येक बफ के लिए एक नया उपवर्ग बनाने की जरूरत है, और यह किसी भी बफ "इंटरैक्शन" के लिए अनुमति नहीं देता है। यही है, अगर मैं क्षति बूस्ट पर एक कैप को लागू करना चाहता था, ताकि भले ही आपके पास 10 अलग-अलग बफ़र्स थे, जो सभी 25% अतिरिक्त नुकसान देते हैं, आप 250% अतिरिक्त के बजाय केवल 100% अतिरिक्त करते हैं।

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

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

विचार? क्या यहां पहले किसी ने काफी मजबूत बफ सिस्टम डिजाइन किया है?

संपादित करें: उत्तर के बारे में:

मैंने मुख्य रूप से अच्छे विवरण और मेरे द्वारा पूछे गए प्रश्न के ठोस उत्तर के आधार पर एक उत्तर का चयन किया, लेकिन प्रतिक्रियाओं को पढ़ने से मुझे कुछ अधिक जानकारी मिली।

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

डियाब्लो 3 (नीचे उल्लेखित) जैसे गेम के लिए, जहां लगभग कोई भी उपकरण किसी बफ़ की ताकत को बदल सकता है, बफ़्स सिर्फ कैरेक्टर स्टैटिस्टिक्स सिस्टम हैं जब भी संभव हो एक अच्छे विचार की तरह लगता है।

चालू-आधारित स्थिति के लिए, घटना-आधारित दृष्टिकोण अधिक उपयुक्त हो सकता है।

किसी भी मामले में, मैं अभी भी उम्मीद कर रहा हूं कि कोई व्यक्ति "ओओ" जादू की गोली के साथ आएगा जो मेरे लिए प्रति टर्न बफ़र +2 चाल दूरी लागू करने की अनुमति देगा , एक सौदा 50% क्षति वापस हमलावर बफ़र, और एक स्वचालित प्रणाली में 3 या अधिक टाइलों से हमला करने पर पास की टाइल के लिए स्वचालित रूप से टेलीपोर्ट एक एकल प्रणाली में बफ़र को अपने स्वयं के सबक्लास में +5 शक्ति बफ़र किए बिना ।

मुझे लगता है कि सबसे करीबी बात यह है कि मैंने जो उत्तर दिया था, लेकिन मंजिल अभी भी खुली है। इनपुट के लिए सभी का धन्यवाद।


मैं इसे एक उत्तर के रूप में पोस्ट नहीं कर रहा हूं, जैसा कि मैं सिर्फ विचार-मंथन कर रहा हूं, लेकिन बफ़र्स की सूची के बारे में कैसे? प्रत्येक बफ़ में एक स्थिर और एक कारक संशोधक होता है। लगातार +10 नुकसान होगा, कारक + 10% + 10% नुकसान को बढ़ावा देने के लिए होगा। आपकी क्षति गणनाओं में, आप कुल संशोधन प्राप्त करने के लिए सभी बफ़र्स को पुन: व्यवस्थित करते हैं, और फिर आप अपनी इच्छानुसार कोई सीमाएँ लागू करते हैं। आप इसे किसी भी प्रकार के परिवर्तनीय विशेषता के लिए करेंगे। हालांकि जटिल चीजों के लिए आपको एक विशेष केस विधि की आवश्यकता होगी।
विलियम मैरीजर

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

1
यदि आपके पास एक "onReiveDamage" विधि है जो एक संदेश प्रणाली, या मैन्युअल या किसी अन्य तरीके से कहलाती है, तो यह आसान होना चाहिए कि आप किससे / क्या नुकसान ले रहे हैं। तो फिर आप इस जानकारी को अपने

ठीक है, मुझे उम्मीद थी कि अमूर्त बफ़ क्लास के लिए प्रत्येक ईवेंट टेम्पलेट में प्रासंगिक पैरामीटर शामिल होंगे। यह निश्चित रूप से काम करेगा, लेकिन मुझे संकोच है क्योंकि यह महसूस करता है कि यह अच्छी तरह से पैमाने पर नहीं होगा। मेरे पास एक कठिन समय है जिसमें कई सौ अलग-अलग शौकीनों के साथ एक MMORPG की कल्पना की जा रही है, प्रत्येक शौकीन के लिए एक अलग वर्ग परिभाषित किया गया है, जिसमें सौ अलग-अलग कार्यक्रम हैं। ऐसा नहीं है कि मैं कई बफ़र्स (शायद 30 के करीब) बना रहा हूं, लेकिन अगर कोई सरल, अधिक सुरुचिपूर्ण, या अधिक लचीला सिस्टम है, तो मैं इसका उपयोग करना चाहता हूं। अधिक लचीली प्रणाली = अधिक दिलचस्प बफ़े / क्षमताएं।
gkimsey

4
यह इंटरैक्शन समस्या का एक अच्छा जवाब नहीं है, लेकिन मुझे लगता है कि डेकोरेटर पैटर्न यहां अच्छी तरह से लागू होता है; बस एक दूसरे के ऊपर अधिक बफ़र्स (सज्जाकार) लागू करें। हो सकता है कि सिस्टम को एक साथ "मर्जिंग" बफ़र्स द्वारा इंटरैक्शन को संभालने के लिए (जैसे 10x 25% मर्ज एक 100% बफ़र में)।
ashes999

जवाबों:


32

यह एक जटिल मुद्दा है, क्योंकि आप कुछ अलग चीजों के बारे में बात कर रहे हैं जो (इन दिनों) एक साथ 'बफ़र्स' के रूप में मिलती हैं:

  • एक खिलाड़ी की विशेषताओं के लिए संशोधक
  • कुछ घटनाओं पर होने वाले विशेष प्रभाव
  • उपरोक्त का संयोजन।

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

फिर मैं इसे संशोधित विशेषताओं तक पहुंचने के लिए फ़ंक्शन के साथ लपेटता हूं। उदा .:

def get_current_attribute_value(attribute_id, criteria):
    val = character.raw_attribute_value[attribute_id]
    # Accumulate the modifiers
    for effect in character.all_effects:
        val = effect.apply_attribute_modifier(attribute_id, val, criteria)
    # Make sure it doesn't exceed game design boundaries
    val = apply_capping_to_final_value(val)
    return val

class Effect():
    def apply_attribute_modifier(attribute_id, val, criteria):
        if attribute_id in self.modifier_list:
            modifier = self.modifier_list[attribute_id]
            # Does the modifier apply at this time?
            if modifier.criteria == criteria:
                # Apply multiplicative modifier
                return val * modifier.amount
        else:
            return val

class Modifier():
    amount = 1.0 # default that has no effect
    criteria = None # applies all of the time

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

मानदंड यह है कि आप "+ 20% बनाम अंडरडाउन" लागू करें - प्रभाव पर UNDEAD मान सेट करें और केवल get_current_attribute_value()तब ही UNDEAD मान पास करें जब आप एक मरे हुए दुश्मन के खिलाफ क्षति रोल की गणना कर रहे हैं।

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

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

# This is a method on a Character, called during combat
def on_receive_damage(damage_info):
    for effect in character.all_effects:
        effect.on_receive_damage(character, damage_info)

class Effect():
    self.on_receive_damage_handler = DoNothing # a default function that does nothing
    def on_receive_damage(character, damage_info):
        self.on_receive_damage_handler(character, damage_info)

def reflect_damage(character, damage_info):
    damage_info.attacker.receive_damage(15)

reflect_damage_effect = new Effect()
reflect_damage_effect.on_receive_damage_handler = reflect_damage
my_character.all_effects.add(reflect_damage_effect)

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


2
उत्कृष्ट विस्तार के लिए +1। यह मेरे प्रश्न का आधिकारिक तौर पर जवाब देने के लिए निकटतम प्रतिक्रिया है जैसा मैंने देखा है। यहाँ मूल सेटअप बहुत अधिक लचीलेपन की अनुमति देता है, और एक छोटा अमूर्त जो अन्यथा गन्दा गेम लॉजिक हो सकता है। जैसा कि आपने कहा था, और अधिक कायरता प्रभाव अभी भी अपने स्वयं के वर्गों की आवश्यकता होगी, लेकिन यह एक विशिष्ट "बफ़" सिस्टम की जरूरतों के थोक को संभालता है, मुझे लगता है।
गिकीसी

यहां छिपे वैचारिक मतभेदों को इंगित करने के लिए +1। ये सभी एक ही इवेंट-आधारित अपडेट लॉजिक के साथ काम नहीं करेंगे। एक पूरी तरह से अलग आवेदन के लिए @ रॉस का जवाब देखें। दोनों को एक दूसरे के बगल में मौजूद होना होगा।
ctietze

22

एक गेम में मैंने एक दोस्त के साथ काम किया एक वर्ग के लिए हमने एक बफ़ / डिबफ सिस्टम बनाया जब उपयोगकर्ता लंबी घास और स्पीड-अप टाइलों में फंस जाता है और क्या नहीं, और कुछ मामूली चीजें जैसे ब्लीड और ज़हर।

यह विचार सरल था, और जब हमने इसे पायथन में लागू किया, तो यह प्रभावी नहीं था।

मूल रूप से, यहाँ बताया गया है:

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

अब वास्तव में दुनिया से शौकीनों को कैसे लागू किया जाए यह एक अलग कहानी है। यहाँ मेरे विचार के लिए भोजन है।


1
यह एक बेहतर स्पष्टीकरण की तरह लगता है जो मैं ऊपर वर्णित करने की कोशिश कर रहा था। यह अपेक्षाकृत सरल है, निश्चित रूप से समझने में आसान है। आपने अनिवार्य रूप से तीन "घटनाओं" का उल्लेख किया है (आगे की सोच के साथ इसे जोड़ने के लिए (OnApply, OnTimeTick, OnExpired)। जैसे, यह हिट और उसके बाद नुकसान की वापसी जैसी चीजों का समर्थन नहीं करेगा, लेकिन यह बहुत सारे बफ़रों के लिए बेहतर है। मैं बल्कि यह नहीं कहूंगा कि मेरे शौकीन क्या कर सकते हैं (जो कि मेरे साथ आने वाली घटनाओं की संख्या को सीमित करता है) जिन्हें मुख्य गेम लॉजिक द्वारा बुलाया जाना है), लेकिन बफ़ स्केलेबिलिटी अधिक महत्वपूर्ण हो सकती है। आपके सहयोग के लिए धन्यवाद!
gkimsey

हाँ, हमने ऐसा कुछ भी लागू नहीं किया। यह वास्तव में साफ-सुथरा लगता है और एक महान अवधारणा (कांटों के शौकीन की तरह)।
रोस

@gkimsey कांटों और अन्य निष्क्रिय बफ़स जैसी चीजों के लिए, मैं आपके मोब क्लास में तर्क को निष्क्रिय या स्वास्थ्य के लिए एक निष्क्रिय स्टेटमेंट के रूप में लागू करूंगा और बफ़र लगाते समय इस स्टेट को बढ़ाऊंगा। यह बहुत सारे मामले को सरल करता है जब आपके पास कई कांटे बफ़र्स के साथ-साथ इंटरफ़ेस को साफ रखते हैं (10 बफ़र्स 10 के बजाय 1 रिटर्न नुकसान दिखाएगा) और बफ़र सिस्टम को सरल रहने देता है।
डी डबलून्स

यह एक लगभग काउंटरिंटिकली सिंपल अप्रोच है, लेकिन मैंने डियाब्लो 3 को बजाते समय अपने बारे में सोचना शुरू किया। मैंने देखा कि जीवन की चोरी, हिट पर जीवन, हाथापाई के हमलावरों को नुकसान, आदि चरित्र विंडो में अपने सभी आँकड़े थे। दी गई, डी 3 में दुनिया में सबसे जटिल बफरिंग सिस्टम या इंटरैक्शन नहीं है, लेकिन यह शायद ही तुच्छ है। इसके बहुत सारे अर्थ निकलते हैं। फिर भी, संभावित रूप से 15 अलग-अलग बफ़र्स हैं जिनमें 12 अलग-अलग प्रभाव हैं जो इस में आते हैं। चरित्र आँकड़े शीट बाहर अजीब गद्दी लगता है ....
gkimsey

11

मुझे यकीन नहीं है कि आप अभी भी इसे पढ़ रहे हैं लेकिन मैं लंबे समय से इस तरह की समस्या से जूझ रहा हूं।

मैंने कई तरह की प्रभावित प्रणालियों को डिज़ाइन किया है। मैं अब संक्षेप में उनके ऊपर जाऊंगा। यह सब मेरे अनुभव पर आधारित है। मैं सभी उत्तरों को जानने का दावा नहीं करता।


स्थैतिक संशोधक

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

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

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

कुल मिलाकर, यह सिर्फ स्टैटिक मॉडिफायर को फ्लैट करने के साथ बहुत अच्छा काम करता है। हालाँकि, कोड का उपयोग किए जाने वाले संशोधक के लिए उचित स्थानों में मौजूद होना चाहिए: getAttack, getMaxHP, getMeleeDamage, और इसी तरह आगे।

जहां यह विधि विफल हो जाती है (मेरे लिए) बफ़र्स के बीच बहुत जटिल बातचीत है। वहाँ एक वास्तविक आसान तरीका नहीं है, यह थोड़ा ऊपर ghettoing को छोड़कर बातचीत है। इसमें कुछ सरल बातचीत की संभावनाएं हैं। ऐसा करने के लिए, आपको स्थिर संशोधक को स्टोर करने के तरीके में संशोधन करना होगा। कुंजी के रूप में एक एनम का उपयोग करने के बजाय, आप स्ट्रिंग का उपयोग करते हैं। यह स्ट्रिंग Enum नाम + अतिरिक्त चर होगा। 10 में से 9 बार, अतिरिक्त चर का उपयोग नहीं किया जाता है, इसलिए आप अभी भी कुंजी के रूप में एनम नाम को बनाए रखते हैं।

चलो एक त्वरित उदाहरण करते हैं: यदि आप मरे हुए प्राणियों के खिलाफ क्षति को संशोधित करने में सक्षम होना चाहते हैं, तो आप इस तरह की एक जोड़ी जोड़ी हो सकते हैं: (DAMAGE_Undead, 10) डैमेज एनम है और अंडरड अतिरिक्त चर है। तो अपने युद्ध के दौरान, आप कुछ ऐसा कर सकते हैं:

dam += attacker.getMod(Mod.DAMAGE + npc.getRaceFamily()); //in this case the race family would be undead

वैसे भी, यह काफी अच्छी तरह से काम करता है और तेज है। लेकिन यह जटिल इंटरैक्शन और हर जगह "विशेष" कोड होने में विफल रहता है। उदाहरण के लिए, "मौत पर टेलीपोर्ट करने के 25% मौके" की स्थिति पर विचार करें। यह एक "काफी" जटिल है। उपरोक्त प्रणाली इसे संभाल सकती है, लेकिन आसानी से नहीं, जैसा कि आपको निम्नलिखित की आवश्यकता है:

  1. निर्धारित करें कि क्या खिलाड़ी के पास यह मॉड है।
  2. कहीं, सफल होने पर टेलीपोर्टेशन निष्पादित करने के लिए कुछ कोड हैं। इस कोड का स्थान अपने आप में एक चर्चा है!
  3. मॉड मैप से सही डेटा प्राप्त करें। मूल्य का क्या अर्थ है? क्या यह वह कमरा है जहाँ वे टेलीपोर्ट करते हैं? अगर किसी खिलाड़ी के पास दो टेलीपोर्ट मॉड हैं तो क्या होगा ?? राशियों को एक साथ नहीं जोड़ा जाएगा ?????? विफलता!

तो यह मेरे अगले एक के लिए मुझे लाता है:


परम जटिल बफ़ सिस्टम

मैंने एक बार खुद से 2 डी MMORPG लिखने की कोशिश की। यह एक बहुत बड़ी गलती थी लेकिन मैंने बहुत कुछ सीखा!

मैंने 3 बार प्रभावित प्रणाली को फिर से लिखा। पहले वाले ने ऊपर के कम शक्तिशाली बदलाव का इस्तेमाल किया। दूसरा वह था जो मैं बात करने जा रहा हूं।

इस प्रणाली में प्रत्येक संशोधन के लिए कक्षाओं की एक श्रृंखला थी, इसलिए चीजें जैसे: ChangeHP, ChangeMaxHP, ChangeHPByPercent, ChangeMaxByPercent। मेरे पास इन लोगों का एक मिलियन था - यहां तक ​​कि TeleportOnDeath जैसी चीजें भी।

मेरी कक्षाओं में ऐसी चीजें थीं जो निम्नलिखित कार्य करेंगी:

  • applyAffect
  • removeAffect
  • checkForInteraction <--- महत्वपूर्ण

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

CheckForInteraction विधि कोड का एक भयानक जटिल टुकड़ा था। प्रत्येक प्रभावित (यानी: चेंजएचपी) कक्षाओं में, यह निर्धारित करने के लिए कोड होगा कि क्या इसे इनपुट प्रभावित द्वारा संशोधित किया जाना चाहिए। तो उदाहरण के लिए, यदि आपके पास कुछ ऐसा था ...।

  • बफ़ 1: हमले पर 10 आग से हुए नुकसान
  • बफ़ 2: सभी आग से होने वाली क्षति को 25% बढ़ाता है।
  • बफ़ 3: 15 से सभी आग क्षति को बढ़ाता है।

CheckForInteraction विधि इन सभी प्रभावों को संभालती है। ऐसा करने के लिए, पास के सभी खिलाड़ियों पर प्रत्येक को प्रभावित करना पड़ता है !! इसका कारण यह है कि मैंने एक क्षेत्र में कई खिलाड़ियों को प्रभावित किया है। इसका मतलब यह है कि ऊपर दिए गए किसी भी विशेष कथनों में कोड कभी भी शामिल नहीं होता है - "अगर हमारी मृत्यु हो गई, तो हमें मृत्यु पर टेलीपोर्ट की जांच करनी चाहिए"। यह प्रणाली सही समय पर अपने आप इसे सही ढंग से संभाल लेगी।

इस प्रणाली को लिखने की कोशिश मुझे 2 महीने की तरह लगी और कई बार सिर फट गया। फिर भी, यह वास्तव में शक्तिशाली था और सामान की एक पागल राशि कर सकता था - खासकर जब आप मेरे खेल में क्षमताओं के लिए निम्नलिखित दो तथ्यों को ध्यान में रखते हैं: 1. उनके पास लक्ष्य सीमाएं थीं (यानी: एकल, स्वयं, केवल समूह, PB AE आत्म , पीबी एई लक्ष्य, लक्षित एई, और इसी तरह)। 2. क्षमताएँ उन पर 1 से अधिक प्रभाव डाल सकती हैं।

जैसा कि मैंने ऊपर उल्लेख किया है, यह इस खेल के लिए 3 डी प्रभावित प्रणाली का दूसरा था। मैं इससे दूर क्यों चला गया?

इस प्रणाली का अब तक का सबसे खराब प्रदर्शन था! यह बहुत धीमी गति से था क्योंकि इसमें प्रत्येक चीज़ के लिए इतनी अधिक जाँच करनी होती थी। मैंने इसे बेहतर बनाने की कोशिश की, लेकिन इसे विफल माना।

इसलिए हम अपने तीसरे संस्करण में आते हैं (और एक अन्य प्रकार की बफ़र प्रणाली):


हैंडलर के साथ जटिल प्रभावित वर्ग

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

प्रभावित वर्ग में सभी रसदार अच्छी चीजें होंगी, जैसे लक्ष्य प्रकार, अवधि, उपयोग की संख्या, निष्पादित करने का मौका और इसी तरह आगे।

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

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

तो इसमें पहली प्रणाली का प्रदर्शन बहुत अधिक है और अभी भी दूसरी (बहुत अधिक नहीं) जैसी दूसरी जटिलता है। जावा में कम से कम, आप MOST मामलों में लगभग पहले वाले (यानी: enum map ( http://docs.oracle.com/javase/6/docs/api/java) होने के लिए कुछ मुश्किल काम कर सकते हैं। Enut के साथ /util/EnumMap.html ) मानों के रूप में प्रभावित करता है और ArrayList के रूप में। यह आपको यह देखने की अनुमति देता है कि क्या आप जल्दी से प्रभावित करते हैं [क्योंकि सूची 0 होगी या मानचित्र में एनम नहीं होगा] और नहीं बिना किसी कारण के खिलाड़ी की प्रभावित सूचियों पर लगातार पुनरावृत्ति करना। मुझे इस बात से कोई फर्क नहीं पड़ता कि यदि इस समय हमें उनकी आवश्यकता है तो मैं इसे प्रभावित करूँगा। यदि बाद में यह समस्या बन जाती है तो मैं इसका अनुकूलन करूंगा।)

मैं वर्तमान में फिर से खोल रहा हूं (फास्टट्रॉम कोड आधार के बजाय जावा में गेम को फिर से लिखना) मूल रूप से मेरा MUD ​​था जो 2005 में समाप्त हो गया था और मैं हाल ही में भाग गया हूं कि मैं अपनी बफ़र प्रणाली को कैसे लागू करना चाहता हूं? मैं इस प्रणाली का उपयोग करने जा रहा हूं क्योंकि इसने मेरे पिछले असफल खेल में अच्छी तरह से काम किया।

खैर, उम्मीद है कि किसी को, कहीं, इन अंतर्दृष्टि के कुछ उपयोगी मिल जाएगा।


6

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

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

हमले के चक्र का एक उदाहरण हो सकता है:

  • खिलाड़ी के हमले (बेस + मोड) की गणना करें;
  • प्रतिद्वंद्वी रक्षा (आधार + मोड) की गणना करें;
  • अंतर करना (और मॉड लागू करना) और आधार क्षति का निर्धारण करना;
  • किसी भी पैरी / कवच प्रभाव (आधार क्षति पर mods) की गणना करें और क्षति को लागू करें;
  • किसी भी पुनरावृत्ति प्रभाव (आधार क्षति पर मॉड) की गणना करें और हमलावर पर लागू करें।

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

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

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


यह एक दिलचस्प दृष्टिकोण है क्योंकि यह उन दो कार्यान्वयनों के बीच कहीं गिरता है जो मैंने माना था - यानी, या तो सिर्फ बफ को काफी सरल स्टेट तक सीमित करना और परिणाम क्षति संशोधक, या बहुत मजबूत लेकिन उच्च-ओवरहेड सिस्टम बनाना जो किसी भी चीज़ को संभाल सकता है। यह सरल इंटरफ़ेस को बनाए रखते हुए "कांटों" की अनुमति देने के लिए पूर्व के विस्तार की तरह है। हालांकि मुझे नहीं लगता कि यह मेरी ज़रूरत के लिए जादू की गोली है, यह निश्चित रूप से ऐसा लगता है कि यह अन्य दृष्टिकोणों की तुलना में बहुत आसान संतुलन बनाता है , इसलिए यह जाने का रास्ता हो सकता है। आपके सहयोग के लिए धन्यवाद!
१०:२६ पर gkimsey

3

मुझे नहीं पता कि आप अभी भी इसे पढ़ रहे हैं, लेकिन यहां बताया गया है कि अब मैं इसे कैसे कर रहा हूं (कोड UE4 और C ++ पर आधारित है)। दो सप्ताह से अधिक समय तक समस्या पर विचार करने के बाद (!), मैंने आखिरकार यह पाया:

http://gamedevelopment.tutsplus.com/tutorials/using-the-composite-design-pattern-for-an-rpg-attributes-system--gamedev-243

और मैंने सोचा, कि अच्छी तरह से, वर्ग / संरचना के भीतर एकल विशेषता को घेरना इतना बुरा विचार नहीं है। हालांकि, ध्यान रखें कि मैं कोड प्रतिबिंब प्रणाली में UE4 बिल्ड का वास्तव में बड़ा फायदा उठा रहा हूं, इसलिए कुछ पुनरावृत्ति के बिना, यह हर जगह उपयुक्त नहीं हो सकता है।

वैसे भी, मैंने एकल संरचना में विशेषता लपेटने से शुरुआत की:

USTRUCT(BlueprintType)
struct GAMEATTRIBUTES_API FGAAttributeBase
{
    GENERATED_USTRUCT_BODY()
public:
    UPROPERTY()
        FName AttributeName;
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Value")
        float BaseValue;
    /*
        This is maxmum value of this attribute.
    */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Value")
        float ClampValue;
protected:
    float BonusValue;
    //float OldCurrentValue;
    float CurrentValue;
    float ChangedValue;

    //map of modifiers.
    //It could be TArray, but map seems easier to use in this case
    //we need to keep track of added/removed effects, and see 
    //if this effect affected this attribute.
    TMap<FGAEffectHandle, FGAModifier> Modifiers;

public:

    inline float GetFinalValue(){ return BaseValue + BonusValue; };
    inline float GetCurrentValue(){ return CurrentValue; };
    void UpdateAttribute();

    void Add(float ValueIn);
    void Subtract(float ValueIn);

    //inline float GetCurrentValue()
    //{
    //  return FMath::Clamp<float>(BaseValue + BonusValue + AccumulatedBonus, 0, GetFinalValue());;
    //}

    void AddBonus(const FGAModifier& ModifiersIn, const FGAEffectHandle& Handle);
    void RemoveBonus(const FGAEffectHandle& Handle);

    void InitializeAttribute();

    void CalculateBonus();

    inline bool operator== (const FGAAttributeBase& OtherAttribute) const
    {
        return (OtherAttribute.AttributeName == AttributeName);
    }

    inline bool operator!= (const FGAAttributeBase& OtherAttribute) const
    {
        return (OtherAttribute.AttributeName != AttributeName);
    }

    inline bool IsValid() const
    {
        return !AttributeName.IsNone();
    }
    friend uint32 GetTypeHash(const FGAAttributeBase& AttributeIn)
    {
        return AttributeIn.AttributeName.GetComparisonIndex();
    }
};

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

इसका वास्तविक महत्वपूर्ण हिस्सा टीएमपी (टीएमएपी हैशेड मैप है)। FGAModifier बहुत सरल संरचना है:

struct FGAModifier
{
    EGAAttributeOp AttributeMod;
    float Value;
};

इसमें संशोधन का प्रकार शामिल है:

UENUM()
enum class EGAAttributeOp : uint8
{
    Add,
    Subtract,
    Multiply,
    Divide,
    Set,
    Precentage,

    Invalid
};

और मान जो अंतिम गणना मूल्य है जिसे हम विशेषता पर लागू करने जा रहे हैं।

हम साधारण फ़ंक्शन का उपयोग करके नया प्रभाव जोड़ते हैं, और फिर कॉल करते हैं:

void FGAAttributeBase::CalculateBonus()
{
    float AdditiveBonus = 0;
    auto ModIt = Modifiers.CreateConstIterator();
    for (ModIt; ModIt; ++ModIt)
    {
        switch (ModIt->Value.AttributeMod)
        {
        case EGAAttributeOp::Add:
            AdditiveBonus += ModIt->Value.Value;
                break;
            default:
                break;
        }
    }
    float OldBonus = BonusValue;
    //calculate final bonus from modifiers values.
    //we don't handle stacking here. It's checked and handled before effect is added.
    BonusValue = AdditiveBonus; 
    //this is absolute maximum (not clamped right now).
    float addValue = BonusValue - OldBonus;
    //reset to max = 200
    CurrentValue = CurrentValue + addValue;
}

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

मेरी सबसे बड़ी पकड़ अभी डैमेजिंग / हीलिंग विशेषता (पूरी तरह से पुनर्गणना को शामिल किए बिना) संभाल रही है, मुझे लगता है कि मेरे पास यह कुछ हल है, लेकिन इसे अभी भी 100% होने के लिए अधिक परीक्षण की आवश्यकता है।

किसी भी एट्रिब्यूट में इस तरह परिभाषित किया जाता है (+ अवास्तविक मैक्रो, यहां छोड़ दिया गया):

FGAAttributeBase Health;
FGAAttributeBase Energy;

आदि।

इसके अलावा, मैं CurrentValue की विशेषता को संभालने के बारे में 100% निश्चित नहीं हूं, लेकिन यह काम करना चाहिए। वे इसे अब कर रहे हैं

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


अपने काम के लिंक और स्पष्टीकरण के लिए धन्यवाद! मुझे लगता है कि आप अनिवार्य रूप से उस ओर बढ़ रहे हैं जो मैं पूछ रहा था। कुछ चीजें जो वसंत को ध्यान में रखते हैं, संचालन का क्रम है (उदाहरण के लिए, 3 "प्रभाव जोड़ें" और 2 "गुणक" समान गुण पर प्रभाव, जो पहले होना चाहिए?), और यह विशुद्ध रूप से विशेषता समर्थन है। पता करने के लिए ट्रिगर्स की धारणा भी है (जैसे "1 एपी खो जाने पर" प्रभाव के प्रकार), लेकिन संभवत: यह एक अलग जांच होगी।
gkimsey

ऑपरेशन का क्रम, केवल विशेषता के बोनस की गणना के मामले में करना आसान है। आप यहां देख सकते हैं कि मेरे पास वहां है और स्विच करें। सभी मौजूदा बोनस (जो जोड़, घटाना, गुणा, भाग कर सकते हैं) पर पुनरावृति के लिए, और फिर उन्हें जमा कर सकते हैं। आप कुछ करें जैसे कि बोनसवैलू = (बोनसवैल्यू * मल्टीप्लीबोनस + एडबोनस-सबट्रैक्टबोनस) / डिवाइडबोनस, या फिर भी आप इस समीकरण को देखना चाहते हैं। प्रविष्टि के एकल बिंदु के कारण इसके साथ प्रयोग करना आसान है। ट्रिगर्स के लिए, मैंने इसके बारे में नहीं लिखा है, क्योंकि यह एक और समस्या है जिसके बारे में मैं विचार करता हूं, और मैंने पहले ही 3-4 (सीमा) की कोशिश की
ukasz Baran

समाधान, उनमें से कोई भी काम नहीं किया जिस तरह से मैं चाहता था (मेरा मुख्य उद्देश्य, उनके लिए डिजाइनर अनुकूल होना है)। मेरा सामान्य विचार, टैग का उपयोग करना और टैग के खिलाफ आने वाले प्रभावों की जांच करना है। यदि टैग मेल खाता है, तो प्रभाव अन्य प्रभाव को ट्रिगर कर सकता है। (टैग सरल मानव पठनीय नाम है, जैसे डैमेज.फायर, अटैक।फिशियल आदि)। कोर पर बहुत आसान अवधारणा है, मुद्दा डेटा को व्यवस्थित कर रहा है, आसानी से सुलभ (खोज के लिए तेज) और नए प्रभावों को जोड़ने में आसानी। आप यहाँ कोड की जाँच कर सकते हैं github.com/iniside/ActionRPGGame (GameAttributes वह मॉड्यूल है जिसमें आपकी रुचि होगी)
zukasz Baran

2

मैंने छोटे पैराग्राफ पर काम किया और सभी वस्तुओं, शक्तियों, बफ़र्स आदि पर 'प्रभाव' पड़ा। एक प्रभाव एक वर्ग था जिसमें 'AddDefense', 'InstantDamage', 'HealHP' आदि के लिए चर थे। शक्तियां, आइटम आदि उस प्रभाव के लिए अवधि को संभालेंगे।

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

उदाहरण के लिए, आपके पास एक बफ है जो रक्षा जोड़ता है। उस बफ़र के लिए न्यूनतम एक EffectID और अवधि होगी। इसे कास्टिंग करते समय, यह निर्दिष्ट अवधि के लिए वर्ण पर EffectID लागू करेगा।

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

यह विधि आपको वर्तमान में लागू होने वाले प्रभावों की सूची पर पुनरावृति करने की अनुमति देती है।

आशा है कि मैंने इस विधि को स्पष्ट रूप से समझाया।


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

2
  1. यदि आप एक एकता उपयोगकर्ता हैं, तो यहां कुछ आरंभ किया जाएगा: http://www.stevegargolinski.com/armory-a-free-and-unfinished-stat-inventory-and-buffdebuff-freeework-for-unity/

Im Buffable / मंत्र / प्रतिभा के रूप में ScriptableOjects का उपयोग कर

public class Spell : ScriptableObject 
{
    public SpellType SpellType = SpellType.Ability;
    public SpellTargetType SpellTargetType = SpellTargetType.SingleTarget;
    public SpellCategory SpellCategory = SpellCategory.Ability;
    public MagicSchools MagicSchool = MagicSchools.Physical;
    public CharacterClass CharacterClass = CharacterClass.None;
    public string Description = "no description available";
    public SpellDragType DragType = SpellDragType.Active; 
    public bool Active = false;
    public int TargetCount = 1;
    public float CastTime = 0;
    public uint EffectRange = 3;
    public int RequiredLevel = 1;
    public virtual void OnGUI()
    {
    }
}

यूनिटीजाइन का उपयोग करना; System.Collections.Generic का उपयोग कर;

सार्वजनिक एनम बफटाइप {बफ, डेबफ} [सिस्टमरिएबलेबल] पब्लिक क्लास बफस्टैट {पब्लिक स्टेट स्टैट = स्टेट.क्रीस्ट्रीम; सार्वजनिक नाव ModValueInPercent = 0.1f; }

public class Buff : Spell
{
    public BuffType BuffType = BuffType.Buff;
    public BuffStat[] ModStats;
    public bool PersistsThroughDeath = false;
    public int AmountPerTick = 3;
    public bool UseTickTimer = false;
    public float TickTime = 1.5f;
    [HideInInspector]
    public float Ticktimer = 0;
    public float Duration = 360; // in seconds
    public float ModifierPerStack = 1.1f;
    [HideInInspector]
    public float Timer = 0;
    public int Stack = 1;
    public int MaxStack = 1;
}

BuffModul:

using System;
using RPGCore;
using UnityEngine;

public class Buff_Modul : MonoBehaviour
{
    private Unit _unit;

    // Use this for initialization
    private void Awake()
    {
        _unit = GetComponent<Unit>();
    }

    #region BUFF MODUL

    public virtual void RUN_BUFF_MODUL()
    {
        try
        {
            foreach (var buff in _unit.Attr.Buffs)
            {
                CeckBuff(buff);
            }
        }
        catch(Exception e) {throw new Exception(e.ToString());}
    }

    #endregion BUFF MODUL

    public void ClearBuffs()
    {
        _unit.Attr.Buffs.Clear();
    }

    public void AddBuff(string buffName)
    {
        var buff = Instantiate(Resources.Load("Scriptable/Buff/" + buffName, typeof(Buff))) as Buff;
        if (buff == null) return;
        buff.name = buffName;
        buff.Timer = buff.Duration;
        _unit.Attr.Buffs.Add(buff);
        foreach (var buffStat in buff.ModStats)
        {
            switch (buff.BuffType)
            {
                case BuffType.Buff:
                    _unit.Attr.AddBuffStatValue(buffStat.Stat, Mathf.RoundToInt((_unit.Attr.StatsBase[buffStat.Stat] + _unit.Attr.StatsItem[buffStat.Stat]) * buffStat.ModValueInPercent));
                    break;
                case BuffType.Debuff:
                    _unit.Attr.RemoveBuffStatValue(buffStat.Stat, Mathf.RoundToInt((_unit.Attr.StatsBase[buffStat.Stat] /*+ unit.character.StatsItem[_stat.stat]*/) * buffStat.ModValueInPercent));
                    break;
            }
            Core.StatController(_unit.Attr, buffStat.Stat);
        }
    }

    public void RemoveBuff(Buff buff)
    {
        foreach (var buffStat in buff.ModStats)
        {
            switch (buff.BuffType)
            {
                case BuffType.Buff:
                    _unit.Attr.RemoveBuffStatValue(buffStat.Stat, Mathf.RoundToInt((_unit.Attr.StatsBase[buffStat.Stat] + _unit.Attr.StatsItem[buffStat.Stat]) * buffStat.ModValueInPercent));
                    break;
                case BuffType.Debuff:
                    _unit.Attr.AddBuffStatValue(buffStat.Stat, Mathf.RoundToInt((_unit.Attr.StatsBase[buffStat.Stat]  /*+ unit.character.StatsItem[_stat.stat]*/) * buffStat.ModValueInPercent));
                    break;
            }
            Core.StatController(_unit.Attr, buffStat.Stat);
        }
        _unit.Attr.Buffs.Remove(buff);
    }

    void CeckBuff(Buff buff)
    {
        buff.Timer -= Time.deltaTime;
        if (!_unit.IsAlive && !buff.PersistsThroughDeath)
        {
            if (buff.ModStats != null)
                foreach (var stat in buff.ModStats)
                {
                    _unit.Attr.StatsBuff[stat.Stat] = 0;
                }

            RemoveBuff(buff);
        }
        if (_unit.IsAlive && buff.Timer <= 0)
        {
            RemoveBuff(buff);
        }
    }
}

0

यह मेरे लिए एक वास्तविक प्रश्न था। मेरे पास इसके बारे में एक विचार है।

  1. जैसा कि पहले कहा गया था, हमें Buffबफ़र्स के लिए एक सूची और एक तर्क updater को लागू करने की आवश्यकता है ।
  2. हमें तब वर्ग के उपवर्गों में सभी विशिष्ट खिलाड़ी सेटिंग्स को हर फ्रेम में बदलना होगा Buff
  3. हम तब परिवर्तनशील सेटिंग्स फ़ील्ड से वर्तमान प्लेयर सेटिंग्स प्राप्त करते हैं।

class Player {
  settings: AllPlayerStats;

  private buffs: Array<Buff> = [];
  private baseSettings: AllPlayerStats;

  constructor(settings: AllPlayerStats) {
    this.baseSettings = settings;
    this.resetSettings();
  }

  addBuff(buff: Buff): void {
    this.buffs.push(buff);
    buff.start(this);
  }

  findBuff(predcate(buff: Buff) => boolean): Buff {...}

  removeBuff(buff: Buff): void {...}

  update(dt: number): void {
    this.resetSettings();
    this.buffs.forEach((item) => item.update(dt));
  }

  private resetSettings(): void {
    //some way to copy base to settings
    this.settings = this.baseSettings.copy();
  }
}

class Buff {
    private owner: Player;        

    start(owner: Player) { this.owner = owner; }

    update(dt: number): void {
      //here we change anything we want in subclasses like
      this.owner.settings.hp += 15;
      //if we need base value, just make owner.baseSettings public but don't change it! only read

      //also here logic for removal buff by time or something
    }
}

इस तरह, Buffउपवर्गों के तर्क में कोई बदलाव नहीं होने के साथ नए खिलाड़ी आँकड़े जोड़ना आसान हो सकता है ।


0

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

सबसे पहले, मुझे लगता है कि एक डिजाइन के नजरिए से ज्यादातर लोग इस बात पर जोर दे रहे हैं कि किस प्रकार के बफ़र्स बनाए जा सकते हैं और वे कैसे लागू होते हैं और ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग के बुनियादी सिद्धांतों को भूल जाते हैं।

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

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

अगर मैं एक बफ / डिबफ सिस्टम डिजाइन कर रहा था तो यहां कुछ चीजें हैं जिन पर मैं विचार करूंगा:

  • प्रभाव का प्रतिनिधित्व करने के लिए एक बफ़ / डेबफ़ वर्ग।
  • एक बफ़ / डेफ़फ़ प्रकार वर्ग बफ़र को प्रभावित करता है और कैसे के बारे में जानकारी शामिल करने के लिए।
  • वर्ण, आइटम और संभवतः स्थान सभी को बफ़र्स और डिबफ़्स शामिल करने के लिए एक सूची या संग्रह संपत्ति की आवश्यकता होगी।

बफ़ / डेफ़फ़ प्रकार में क्या होना चाहिए, इसके लिए कुछ विवरण:

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

यह केवल एक शुरुआत है, लेकिन वहां से आप केवल वही परिभाषित कर रहे हैं जो आप चाहते हैं और अपने सामान्य गेम स्टेट का उपयोग करके उस पर कार्य कर रहे हैं। उदाहरण के लिए, मान लीजिए कि आप एक शापित आइटम बनाना चाहते हैं, जो गति को कम कर दे ...

जब तक मैंने उचित प्रकार डाल दिया है तब तक यह एक बफ़ रिकॉर्ड बनाने के लिए सरल है जो कहता है:

  • प्रकार: अभिशाप
  • उद्देश्य: आइटम
  • स्टेटकैटेरी: उपयोगिता
  • स्टैडफाइड: मूवमेंटस्पीड
  • अवधि: अनंत
  • ट्रिगर: चालू

और आगे, और जब मैं एक बफ बनाता हूं तो मैं इसे बफ़ट ऑफ शाप असाइन करता हूं और बाकी सब कुछ इंजन पर निर्भर है ...

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