गेम इंजन में हार्ड कोडिंग से कैसे बचें


22

मेरा प्रश्न कोडिंग प्रश्न नहीं है; यह सामान्य रूप से गेम इंजन डिज़ाइन के सभी पर लागू होता है।

हार्ड कोडिंग से आप कैसे बचें?

यह सवाल जितना लगता है उससे कहीं ज्यादा गहरा है। कहो, यदि आप एक गेम चलाना चाहते हैं जो ऑपरेशन के लिए आवश्यक फ़ाइलों को लोड करता है, तो आप load specificfile.wadइंजन के कोड में कुछ कहने से कैसे बचेंगे? इसके अलावा, जब फ़ाइल लोड की जाती है, तो आप यह कहने से कैसे बचते हैं load aspecificmap in specificfile.wad?

यह प्रश्न इंजन के सभी डिज़ाइन पर बहुत अधिक लागू होता है, और इंजन के कम से कम हार्ड कोडित होने चाहिए। इसे प्राप्त करने का सबसे अच्छा तरीका क्या है?

जवाबों:


42

डेटा चालित कोडिंग

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

तुम कैसे खोजोगे aspecificmap? क्योंकि यह एक डेटा फ़ाइल में है जो मैप आईडी और उनके ऑन-डिस्क संसाधनों को सूचीबद्ध करता है।

केवल "कोर" संसाधनों का एक विशेष रूप से छोटा सेट होना चाहिए जो हार्ड-कोडिंग से बचने के लिए वैध रूप से कठिन या असंभव हो। थोड़े से काम के साथ, यह एकल हार्ड-कोडित डिफ़ॉल्ट एसेट नाम जैसे main.wadया पसंद तक सीमित हो सकता है । इस फ़ाइल को संभवतः गेम, उर्फ ​​में कमांड-लाइन तर्क पास करके रनटाइम में बदला जा सकता है game.exe -wad mymain.wad

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

हमारे कोडबेस से एक ठोस उदाहरण के रूप में, हमारे पास एक "वैश्विक डेटा" ऑब्जेक्ट है जो संसाधन डेटाबेस से लोड किया गया है (जो स्वयं ./resourcesफ़ोल्डर डिफ़ॉल्ट रूप से है, लेकिन कमांड लाइन तर्क के साथ अतिभारित किया जा सकता है)। इस वैश्विक डेटा का संसाधन डेटाबेस आईडी कोडबेस में एकमात्र आवश्यक हार्ड-कोडेड संसाधन नाम है (हमारे पास अन्य हैं क्योंकि कभी-कभी प्रोग्रामर आलसी हो जाते हैं, लेकिन हम आम तौर पर अंततः उन फिक्सिंग / हटाने को समाप्त कर देते हैं)। यह वैश्विक डेटा ऑब्जेक्ट उन घटकों से भरा है जिनका एकमात्र उद्देश्य कॉन्फ़िगरेशन डेटा प्रदान करना है। घटक में से एक यूआई ग्लोबल डेटा घटक है जिसमें कई अन्य कॉन्फ़िगरेशन आइटमों के बीच सभी मुख्य यूआई संसाधनों (फोंट, फ्लैश फाइलें, आइकन, स्थानीयकरण डेटा, आदि) के लिए संसाधन हैंडल शामिल हैं। जब कोई UI डेवलपर मुख्य UI संपत्ति का नाम बदलने का निर्णय लेता /ui/mainmenu.swfहै/ui/lobby.swfवे सिर्फ उस वैश्विक डेटा संदर्भ को अपडेट करते हैं; किसी भी इंजन कोड को बदलने की जरूरत नहीं है।

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

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

इस पूरी बात का एक कोना कोना लिपियों में है। इंजन डेटा-संचालित करना आसान है, लेकिन यह वैचारिक रूप से आसान है, लेकिन मैंने बहुत सारे प्रोजेक्ट्स (एएए के शौक) देखे हैं, जहां स्क्रिप्ट्स को डेटा माना जाता है और इसलिए केवल संसाधन पथों का अंधाधुंध उपयोग करने के लिए "अनुमति" दी जाती है। ऐसा मत करो। अगर किसी Lua फ़ाइल को एक संसाधन की आवश्यकता होती है और यह सिर्फ एक फ़ंक्शन को कॉल करती है, जैसे textures.lua("/path/to/texture.png")कि परिसंपत्ति पाइपलाइन को यह जानने में बहुत परेशानी होती है कि स्क्रिप्ट को /path/to/texture.pngसही ढंग से संचालित करने की आवश्यकता है और उस बनावट को अप्रयुक्त और अनावश्यक मान सकता है। लिपियों को किसी अन्य कोड की तरह माना जाना चाहिए: संसाधनों या तालिकाओं सहित किसी भी डेटा को कॉन्फ़िगरेशन प्रविष्टि में निर्दिष्ट किया जाना चाहिए जिसे इंजन और संसाधन पाइपलाइन निर्भरता के लिए निरीक्षण कर सकते हैं। इसके foo.luaबजाय "लोड स्क्रिप्ट " कहने वाले डेटा को कहना चाहिए "foo.luaऔर इसे ये मानदंड दें "जहां मापदंडों में आवश्यक कोई भी संसाधन शामिल हैं। यदि कोई स्क्रिप्ट उदाहरण के लिए दुश्मनों को बेतरतीब ढंग से जगाती है, तो उस कॉन्फ़िगरेशन फ़ाइल से स्क्रिप्ट में संभावित दुश्मनों की सूची पास करें। इंजन तब स्तर के साथ दुश्मनों को पूर्व-लोड कर सकता है ( चूंकि यह संभावित स्पॉन की पूरी सूची जानता है) और संसाधन पाइपलाइन खेल के साथ सभी दुश्मनों को बंडल करना जानता है (क्योंकि वे निश्चित रूप से कॉन्फ़िगरेशन डेटा द्वारा संदर्भित हैं)। यदि स्क्रिप्ट पथ नामों के तार उत्पन्न करती है और बस एक loadफ़ंक्शन को कॉल करती है इंजन और न ही संसाधन पाइपलाइन में विशेष रूप से यह जानने का कोई तरीका है कि स्क्रिप्ट को लोड करने के लिए कौन सी संपत्ति की कोशिश हो सकती है।


अच्छा जवाब, बहुत व्यावहारिक है, और यह भी लोगों को समझाता है कि इसे लागू करते समय क्या गलतियां और त्रुटियां होती हैं! +1
whn

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

12

ठीक उसी तरह जब आप सामान्य कार्यों में हार्डकोडिंग से बचते हैं।

आप पैरामीटर पास करते हैं और आप कॉन्फ़िगरेशन फ़ाइलों में अपनी जानकारी रखते हैं।

उस स्थिति में, इंजन लिखने और क्लास लिखने के बीच सॉफ्टवेयर इंजीनियरिंग में बिल्कुल कोई अंतर नहीं है।

MgrAssets
public:
  errorCode loadAssetFromDisk( filePath )
  errorCode getMap( mapName, map& )

private:
  maps[name, map]

फिर आपका क्लाइंट कोड एक "मास्टर" कॉन्फ़िगरेशन फ़ाइल पढ़ता है ( यह एक या तो हार्ड कोडित है या कमांड लाइन तर्क के रूप में पारित किया गया है) जिसमें जानकारी शामिल है जो बताती है कि संपत्ति फाइलें कहां हैं और उनके पास क्या नक्शा है।

वहां से, सब कुछ "मास्टर" कॉन्फ़िगरेशन फ़ाइल द्वारा संचालित होता है।


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

3

मुझे अन्य उत्तर पसंद हैं इसलिए मैं थोड़ा विपरीत होने जा रहा हूं। ;)

आप अपने इंजन में अपने डेटा के बारे में जानकारी कोडिंग से नहीं बचा सकते हैं। जहां से भी जानकारी मिलती है, इंजन को इसकी तलाश करनी चाहिए। हालाँकि, आप वास्तविक जानकारी को अपने इंजन में एन्कोडिंग करने से बच सकते हैं।

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

हालाँकि, एक "अच्छी तरह से निर्मित" इंजन कॉन्फ़िगरेशन डेटा और इसे संदर्भित करने वाले डेटा को स्वैप करके कई अलग-अलग खेलों के लिए काम कर सकता है।

तो मंत्र कठिन कोडिंग से बचने के लिए इतना नहीं है क्योंकि यह सुनिश्चित करना है कि आप कम से कम प्रयास के साथ बदलाव कर सकते हैं।

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

struct Weapon
{
    enum IconID icon;
    enum ModelID model;
    int damage;
    int rateOfFire;
    // etc...
};

const struct Weapon g_weapons[] =
{
    { ICON_PISTOL, MODEL_PISTOL, 5, 6 },
    { ICON_RIFLE, MODEL_RIFLE, 10, 20 },
    // etc...
};

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


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

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

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

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

2
दरअसल, मैंने आपके उत्तर को गलत ठहराया; कोई वास्तविक तर्क नहीं है । मैं सिर्फ यह नोट करना चाहता था कि JSON को पार्स करना मुश्किल नहीं है। फिर से पढ़ना, मुझे लगता है कि मैं ज्यादातर स्निपेट का जवाब दे रहा था "लेकिन फिर आपको पार्सिंग और अनुवाद और उस सभी जैज़ को करने की ज़रूरत है।" लेकिन मैं इस बात से सहमत हूं कि व्यक्तिगत प्रोजेक्ट गेम्स और इस तरह के, YAGNI। :)
वाइल्डकार्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.