संस्करण के साथ समान गेम में गेम कोड से गेम इंजन को अलग करना


15

मेरे पास एक समाप्त खेल है, जिसे मैं अन्य संस्करणों में अस्वीकार करना चाहता हूं। ये इसी तरह के खेल होंगे, कमोबेश एक ही तरह के डिजाइन, लेकिन हमेशा नहीं, मूल रूप से चीजें कभी-कभी छोटी, कभी बड़ी हो सकती हैं।

मैं चाहूंगा कि कोर कोड को खेल से अलग रूप दिया जाए, ताकि अगर मैं कहूं कि मैं गेम ए में पाया गया बग ठीक करूं, तो यह गेम बी में मौजूद होगा।

मैं इसे प्रबंधित करने का सबसे अच्छा तरीका खोजने की कोशिश कर रहा हूं। मेरे शुरुआती विचार इस प्रकार हैं:

  • एक engineमॉड्यूल / फ़ोल्डर / जो कुछ भी बनाएं , जिसमें वह सब कुछ शामिल है जिसे सामान्यीकृत किया जा सकता है और बाकी गेम से 100% स्वतंत्र है। इसमें कुछ कोड शामिल होंगे, लेकिन जेनेरिक संपत्ति भी होगी जो खेल के बीच साझा की जाती हैं।
  • इस इंजन को अपनी gitरिपॉजिटरी में रखें , जिसे गेम्स में शामिल किया जाएगाgit submodule

मैं जिस हिस्से से जूझ रहा हूं, वह है कि बाकी कोड को कैसे प्रबंधित किया जाए। मान लें कि आपके पास अपना मेनू दृश्य है, यह कोड गेम-विशिष्ट है, लेकिन इसमें से अधिकांश सामान्य हो जाता है और अन्य खेलों में पुन: उपयोग किया जा सकता है। मैं इसे नहीं डाल सकता engine, लेकिन प्रत्येक खेल के लिए इसे फिर से कोड करना अक्षम होगा।

हो सकता है कि कुछ प्रकार की git शाखाओं का उपयोग करना भिन्नता को प्रबंधित करने के लिए प्रभावी हो, लेकिन मुझे नहीं लगता कि यह जाने का सबसे अच्छा तरीका है।

किसी के पास कुछ विचार हैं, साझा करने के लिए अनुभव या उसके बारे में कुछ भी?


आपका इंजन किस भाषा में है? कुछ भाषाओं में पैकेज मैनेजर होते हैं जो git सबमॉड्यूल्स का उपयोग करने की तुलना में अधिक समझ में आता है। उदाहरण के लिए, NodeJS में npm है (जो स्रोतों के रूप में Git repos को लक्षित कर सकता है)।
डैन पेंट्री

क्या आपका सवाल यह है कि जेनेरिक कोड को कैसे कॉन्फ़िगर करें या "सेमी-जेनेरिक" कोड को कैसे कॉन्फ़िगर करें या कोड को कैसे आर्किटेक्ट करें, कोड को कैसे डिजाइन करें या क्या करें?
डंक

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

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

जवाबों:


13

एक इंजन मॉड्यूल / फ़ोल्डर / जो कुछ भी बनाएं, जिसमें वह सब कुछ शामिल है जिसे सामान्यीकृत किया जा सकता है और बाकी गेम से 100% स्वतंत्र है। इसमें कुछ कोड शामिल होंगे, लेकिन जेनेरिक संपत्ति भी होगी जो खेल के बीच साझा की जाती हैं।

इस इंजन को अपने स्वयं के गिट रिपॉजिटरी में रखें, जो कि गेम में एक सबमॉड्यूल के रूप में शामिल किया जाएगा

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

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

स्रोत नियंत्रण और बायनेरिज़ पर ध्यान दें

बस याद रखें कि यदि आप अपनी द्विआधारी परिसंपत्तियों को अक्सर बदलने की उम्मीद कर रहे हैं, तो आप इसे पाठ-आधारित स्रोत नियंत्रण जैसे गिट में नहीं डालना चाह सकते हैं। बस 'कह रहे हैं ... बायनेरिज़ के लिए बेहतर समाधान हैं। अपने "इंजन-सोर्स" रिपॉजिटरी को साफ रखने में मदद के लिए अब आप जो सबसे आसान काम कर सकते हैं, वह यह है कि एक अलग "इंजन-बायनेरी" रिपॉजिटरी जो केवल बायनेरिज़ रखती है, जिसे आप अपने प्रोजेक्ट में सबमॉडल के रूप में भी शामिल करते हैं। इस तरह से आप अपने "इंजन-सोर्स" रिपॉजिटरी में किए गए प्रदर्शन-नुकसान को कम करते हैं, जो हर समय बदल रहा है और जिस पर इस प्रकार आपको तेज पुनरावृत्तियों की आवश्यकता होती है: कमिट, पुश, पुल इत्यादि। सोर्स कंट्रोल मैनेजमेंट सिस्टम जैसे टेक्स्ट डेल्टस पर काम करते हैं। , और जैसे ही आप बायनेरिज़ का परिचय देते हैं, आप एक पाठ परिप्रेक्ष्य से बड़े पैमाने पर डेल्टा का परिचय देते हैं - जो अंततः आपको देव समय खर्च करता है।गिटलैब एनेक्स । Google का आपका मित्र


वे वास्तव में अक्सर नहीं बदलते हैं, लेकिन मुझे इसमें दिलचस्पी है। मैं बाइनरी वर्जनिंग के बारे में कुछ नहीं जानता। क्या समाधान हैं?
मल्हारक

@ मल्हारक ने आपकी टिप्पणी का जवाब देने के लिए संपादन किया।
इंजीनियर

@ मल्हारक यहाँ इस विषय पर जानकारी का एक अच्छा सा है
इंजीनियर

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

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

6

कुछ बिंदु पर एक इंजन जरूरी है और खेल के बारे में सामान जानना चाहिए। मैं यहाँ एक स्पर्शरेखा पर जाऊँगा।

एक आरटीएस में संसाधन लें। एक गेम हो सकता है Creditsऔर Crystalदूसरा MetalऔरPotatoes

आपको ओओ कॉन्सेप्ट का सही इस्तेमाल करना चाहिए और अधिकतम के लिए जाना चाहिए। कोड-पुन: उपयोग। यह स्पष्ट है कि यहां एक अवधारणा Resourceमौजूद है।

इसलिए हम तय करते हैं कि संसाधन निम्नलिखित हैं:

  1. मुख्य लूप में एक हुक खुद को बढ़ाने / घटाने के लिए
  2. वर्तमान राशि प्राप्त करने का एक तरीका (रिटर्न int)
  3. मनमाने ढंग से घटाना / जोड़ने का एक तरीका (संसाधनों को स्थानांतरित करने वाले खिलाड़ी, खरीद ...)

ध्यान दें कि इस धारणा को Resourceएक खेल में मारता है या अंक का प्रतिनिधित्व कर सकता है! यह बहुत शक्तिशाली नहीं है।

अब एक गेम के बारे में सोचते हैं। हम पेनी में काम करने और आउटपुट में दशमलव बिंदु जोड़कर मुद्रा की तरह कर सकते हैं। हम जो नहीं कर सकते हैं वह "तात्कालिक" संसाधन हैं। जैसे "पावर ग्रिड पीढ़ी"

कहते हैं कि आप InstantResourceसमान विधियों के साथ एक वर्ग जोड़ते हैं । अब आप संसाधनों के साथ अपने इंजन को प्रदूषित कर रहे हैं (शुरू कर रहे हैं)।


समस्या

फिर से आरटीएस का उदाहरण लें। मान लीजिए कि जो खिलाड़ी किसी Crystalऔर खिलाड़ी को कुछ दान करता है । आप कुछ ऐसा करना चाहते हैं:

if(transfer.target == engine.getPlayerId()) {
    engine.hud.addIncoming("You got "+transfer.quantity+" of "+
        engine.resourceDictionary.getNameOf(transfer.resourceId)+
        " from "+engine.getPlayer(transfer.source).name);
}
engine.getPlayer(transfer.target).getResourceById(transfer.resourceId).add(transfer.quantity)
engine.getPlayer(transfer.source).getResourceById(transfer.resourceId).add(-transfer.quantity)

हालाँकि यह वास्तव में काफी गड़बड़ है। यह सामान्य उद्देश्य है, लेकिन गड़बड़ है। पहले से ही हालांकि यह एक resourceDictionaryअर्थ लगाता है कि अब आपके संसाधनों के नाम होने हैं! और यह प्रति खिलाड़ी है, इसलिए आपके पास टीम संसाधन नहीं हो सकते।

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

engine.getPlayer(transfer.target).crystal().receiveDonation(transfer)
engine.getPlayer(transfer.source).crystal().sendDonation(transfer)

एक वर्ग के साथ Playerऔर एक वर्ग CurrentPlayerजहां CurrentPlayerकी crystalवस्तु स्वचालित रूप से / स्थानांतरण के लिए HUD पर सामान दिखा देंगे दान के भेज दिया।

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


अंतिम टिप्पणी

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

  1. "कोर" - यह इंजन की पाठ्यपुस्तक की परिभाषा है, यह ईवेंट हुक के साथ एक दृश्य ग्राफ है, यह शेड्स और नेटवर्क पैकेट और खिलाड़ियों की एक अमूर्त धारणा से संबंधित है
  2. "गेमकोर" - यह खेल के प्रकार के लिए बहुत सामान्य है, लेकिन सभी खेलों के लिए नहीं - उदाहरण के लिए आरटीएस में संसाधन या एफपीएस में गोला बारूद। खेल का तर्क यहीं से रिसना शुरू होता है। यह वह जगह है जहाँ संसाधनों की हमारी पूर्व धारणा होगी। हमने इन चीजों को जोड़ा है जो अधिकांश आरटीएस संसाधनों के लिए समझ में आता है।
  3. "GameLogic" वास्तविक खेल के लिए बहुत विशिष्ट है। आप जैसे नामों के साथ चर मिल जाएगा creatureया shipया squad। का उपयोग करते हुए विरासत आप वर्गों है कि सभी 3 परतें अवधि मिलेगा (उदाहरण के लिए Crystal एक है Resource जो एक है GameLoopEventListener कहते हैं)
  4. "एसेट्स" ये किसी भी अन्य खेल के लिए बेकार हैं। उदाहरण के लिए एआई लिपियों को आधा जीवन 2 में लें, वे एक ही इंजन के साथ आरटीएस में उपयोग नहीं होने जा रहे हैं।

पुराने इंजन से नया गेम बनाना

यह बहुत आम है। चरण 1 परतों 3 और 4 (और 2 को चीरना है अगर खेल एक अलग प्रकार है) मान लीजिए कि हम एक पुराने आरटीएस से आरटीएस बना रहे हैं। हमारे पास अभी भी संसाधन हैं, न कि क्रिस्टल और सामान - इसलिए परतों 2 और 1 में आधार वर्ग अभी भी समझ में आता है, 3 और 4 में संदर्भित सभी क्रिस्टल को त्याग दिया जा सकता है। तो हम करते हैं। हालाँकि हम इसे एक संदर्भ के रूप में जाँच सकते हैं कि हम क्या करना चाहते हैं।


परत 1 में प्रदूषण

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


परतों को वर्गीकृत करना

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

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


1

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

3 चीजें हैं जो (लगभग) हमेशा एक मेनू दृश्य पर मौजूद होती हैं: पृष्ठभूमि, खेल का लोगो और मेनू स्वयं। ये चीजें आमतौर पर खेल के आधार पर भिन्न होती हैं। इस सामग्री के लिए आप क्या कर सकते हैं अपने इंजन में एक मेनूस्क्रीनराइटर बना सकते हैं, जिसमें 3 ऑब्जेक्ट पैरामीटर हैं: बैकग्राउंड, लोगो और मेनू। इन 3 भागों की मूल संरचना भी आपके इंजन का हिस्सा है, लेकिन आपका इंजन वास्तव में यह नहीं कहता है कि ये भाग कैसे उत्पन्न होते हैं, बस आपको उन्हें क्या पैरामीटर देना चाहिए।

फिर अपने वास्तविक गेम कोड में, आप एक BackGround, एक लोगो और एक मेनू के लिए ऑब्जेक्ट बनाते हैं, और आप इसे अपने MenuScreenGenerator को पास करते हैं। फिर से, आपका गेम खुद को हैंडल नहीं करता है कि मेनू कैसे उत्पन्न होता है, यह इंजन के लिए है। आपके खेल को केवल इंजन को यह बताने की आवश्यकता है कि उसे कैसा दिखना चाहिए और यह कहाँ होना चाहिए।

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

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