हाल ही में, एकता 3 डी पर एक गेम के लिए जिसे हम विकसित कर रहे थे,
डीएलसी सिस्टम को जोड़ना आवश्यक हो गया। हालांकि यह शुरुआत में जितना आसान लग रहा था, उससे बहुत दूर था, हम सफलतापूर्वक उन समस्याओं से मुकाबला कर रहे थे, जो खेल उठी और खेल सोने में बदल गया। इस लेख में मैं डीएलसी कार्यान्वयन के हमारे संस्करण को प्रस्तुत करना चाहता हूं, जो समस्याएं उत्पन्न हुई हैं और हम उन्हें कैसे हल करते हैं, के बारे में बात करते हैं।
समस्या का बयान
खेल में एक स्टोर होता है जहां एक खिलाड़ी खेल या वास्तविक मुद्रा के लिए चीजें खरीदता है। स्टोर में 200 से अधिक आइटम हैं। जब कोई खिलाड़ी खेल में प्रवेश करता है, तो उसके पास स्टोर में 20 वस्तुएं उपलब्ध होती हैं। अगर इंटरनेट है, तो उपयोगकर्ता के ज्ञान के बिना खेल डीएलसी के लिए सर्वर को पोल करता है और यदि कोई है, तो इसे पृष्ठभूमि में डाउनलोड करता है। जब खिलाड़ी स्टोर में फिर से प्रवेश करता है, तो वह डीएलसी से सभी नई चीजें देखेगा।
अभी भी स्थानों का एक सेट है। प्रत्येक स्थान पर बनावट और .asset फ़ाइलों का एक सेट है। डीएलसी के माध्यम से नए स्थानों को भी जोड़ा जाना चाहिए।
डीएलसी से संसाधन लोडिंग समकालिक होना चाहिए।
प्लेटफ़ॉर्म: iOS (iPhone 3GS और इसके बाद के संस्करण) और Android (सैमसंग गैलेक्सी एस और ऊपर)।
डीएलसी सामग्री और खेल में इसके साथ काम करें
गेम में, चीजें पूरी तरह से आइटमडेटा.टेक्स्ट फ़ाइल द्वारा निर्धारित की जाती हैं, जिसमें चीजों और उनके बनावट के बारे में जानकारी होती है। इसका मतलब यह है कि प्रत्येक डीएलसी में उन चीजों के एक सेट के साथ एक आइटमडेटाटेक्स्ट फ़ाइल होगी जो इन चीजों के लिए डीएलसी + टेस्ट में हैं। और जब स्टोर चीजों के डेटाबेस का अनुरोध करता है, तो हम सभी डीएलसी से सभी पाठ फ़ाइलों को गोंद करते हैं और इसे यह फाइल देते हैं।
इसी प्रकार स्थानों के लिए स्थानों की एक सूची और विशेषताओं के साथ एक locationdata.txt फ़ाइल है + उनके लिए बनावट और संपत्ति फाइलें।
गेम लॉजिक में संसाधनों को लोड करने के लिए संबंधित C # कोड इस तरह दिखेगा:
public String GetItemDataBase() { if(DLCManager.isDLCLoaded() == true) {
इसी तरह, एक बनावट का अनुरोध करते समय, हम डीएलसी में इसकी उपस्थिति की जांच करते हैं। यदि यह वहां है, तो लोड करें, अन्यथा खेल संसाधनों से लोड करें। यदि वहाँ नहीं है, तो कुछ डिफ़ॉल्ट रूप से लोड करें।
public Texture GetTexture(string txname) { Texture tx = null; if(DLCManager.isDLCLoaded() == true) { tx = DLCManager.GetTextureFromDLC(txname); } if(tx == null) { tx = Resources.Load(txname) as Texture; } if(tx == null) { Assert(tx, “Texture not find: ” + txname); tx = Resources.Load(kDefaultItemTexturePath) as Texture; } return tx; }
इसी तरह .asset फ़ाइलों के लिए एक GetAsset (स्ट्रिंग एसेटनेम) फ़ंक्शन होगा। इसका कार्यान्वयन समान होगा, इसलिए इसे छोड़ें।
डीएलसी फ़ाइल
हमने तय किया कि डीएलसी में क्या होना चाहिए। यह तय करना बाकी है कि इसे स्टोर करना क्या है।
पहला विकल्प डीएलसी को ज़िप आर्काइव के रूप में संग्रहीत करना है। प्रत्येक संग्रह में - एक पाठ फ़ाइल + एन बनावट। वीडियो मेमोरी को बचाने के लिए बनावट पीवीआरटीसी प्रारूप में होनी चाहिए। लेकिन यहाँ हमें पहली समस्या है - एकता केवल PNG या JPG फॉर्मेट [ लिंक ] में फाइल सिस्टम से टेक्सट डाउनलोड करने का समर्थन करती है। फिर बनावट को PVRTC बनावट [ लिंक ] पर लिखा जा सकता है। यह एक धीमी प्रक्रिया है क्योंकि वास्तविक समय में पीवीआर में रूपांतरण की आवश्यकता है। इसके अलावा, क्योंकि यह DLC की फ़ाइलों को DLC में संग्रहीत करने की योजना बना रहा है, और संभवतः गेम स्तर (.scene), इस तरह की विधि पूरी तरह से अनुपयुक्त है।
दूसरा विकल्प AssetBundle का उपयोग करना है। यह समाधान खेलों में डीएलसी के लिए आदर्श है।
दस्तावेज़ीकरण को देखते हुए, इसके बहुत सारे फायदे हैं:
- यह किसी भी एकता संसाधनों को संग्रहीत कर सकता है, जिसमें वांछित बनावट प्रारूप (जो हमें ज़रूरत है) में संकुचित होता है।
- यह अच्छा संपीड़न के साथ एक संग्रह है।
- सरल और सुविधाजनक उपयोग करने के लिए।
- यह संस्करण पैरामीटर और हैश राशि का समर्थन करता है (जब लोडफ्रॉमचैकरडाउनलोड फ़ंक्शन द्वारा लोड किया जाता है), जो डीएलसी के संस्करण नियंत्रण के लिए सुविधाजनक है
Minuses के, केवल उस AssetBundle को एकता के प्रो संस्करण की आवश्यकता है और एन्क्रिप्शन का समर्थन नहीं करता है । हमने इस निर्णय पर ध्यान केन्द्रित करने का निर्णय लिया, क्योंकि यह स्पष्ट रूप से अधिक आकर्षक है और हमें हमारी सभी समस्याओं को हल करने की अनुमति देता है।
कार्यान्वयन (विकल्प 1)
शुरुआत करने के लिए, डीएलसी प्रणाली का एक परीक्षण संस्करण सबसे बुनियादी कार्यक्षमता के साथ बनाया गया था।
सबसे पहले, स्टोर आइटम और स्थान फ़ाइलों के सभी 200-अजीब बनावट एक एसेटबंडल में पैक किए गए थे और सर्वर पर अपलोड किए गए थे। फ़ाइल लगभग 200 mb निकली। AssetBundle में पैकेजिंग संपादक में एक स्क्रिप्ट द्वारा किया गया था। AssetBundle में रिसोर्स पैकेजिंग कैसे करें यह अच्छी तरह से प्रलेखन में वर्णित है। आप AssetBundle बनाने के लिए मेरी स्क्रिप्ट का भी उपयोग कर सकते हैं।
खेल शुरू करने के बाद, हम निम्नलिखित कदम उठाते हैं:
- सबसे पहले आपको सर्वर से डीएलसी डाउनलोड करना होगा। हम एकता मैनुअल से कोड के अनुसार ऐसा करते हैं। अगला, हम डाउनलोड किए गए डेटा को भविष्य के उपयोग के लिए डिस्क पर एक फ़ाइल में लिखते हैं।
इस कोड के साथ, हमें iPhone 3GS जैसे कम उपकरणों पर मेमोरी क्रैश होने की बहुत संभावना है, क्योंकि WWW वर्ग बफर लोडिंग का समर्थन नहीं करता है और मेमोरी में सभी लोड की गई जानकारी संग्रहीत करता है। हम इस समस्या के बारे में थोड़ी देर बाद बात करेंगे। जबकि हम इस पल को याद करते हैं और आगे बढ़ते हैं।
- डीएलसी से संसाधन लोड करना।
अब हमें GetTextureFromDLC (), GetAssetFromDLC () और GetTextFileFromAllDLCs () फ़ंक्शन को परिभाषित करने की आवश्यकता है। हम बाद की परिभाषा को छोड़ देंगे, क्योंकि यह लोड किए गए संसाधन के प्रकार को छोड़कर शायद ही पहले वाले से अलग होगा।
GetTextureFromDLC फ़ंक्शन का मुख्य कार्य डीएलसी से नाम से एक बनावट को समकालिक रूप से लोड करना है।
आइए इसे निम्नानुसार परिभाषित करने का प्रयास करें।
public Texture GetTextureFromDLC(String textureName) {
उपरोक्त कोड अभी तक AssetBundle से एक संसाधन को समान रूप से लोड करने का एकमात्र संभव तरीका है। और जैसा कि यह निकला, बहुत सारी बारीकियां हैं। चलो उन्हें क्रम में क्रमबद्ध करें।
AssetBundle.CreateFromFile
प्रलेखन के अनुसार डिस्क से संपत्तियां डाउनलोड करता है। लेकिन वहाँ एक चेतावनी है - "केवल असम्पीडित संपत्ति बंडलों को इस फ़ंक्शन द्वारा समर्थित किया जाता है।" इस प्रकार, केवल असम्पीडित असेंबली को समान रूप से लोड किया जा सकता है। जो सर्वर से ट्रैफ़िक और डीएलसी बूट समय को महत्वपूर्ण रूप से बढ़ा देगा। इसके अलावा, एकता संपुटित से असम्पीडल को असम्पीडित में बदलने का समर्थन नहीं करती है, इसलिए यह एक संपीड़ित बंडल को डाउनलोड करने के लिए काम नहीं करेगा, और फिर इसे क्लाइंट पर अनपैक करें।
पाठक आश्चर्यचकित हो सकता है कि एसेटबंडल को एसिंक्रोनस रूप से लोड क्यों नहीं किया गया है, उदाहरण के लिए, लोडफ्रोमकैचऑर्ड डाऊनलोड फ़ंक्शन के साथ, और फिर इसके लिए आवश्यक संसाधनों को सिंक्रोनाइज़ करें। आखिरकार, यह तर्कसंगत है कि फ़ाइल सिस्टम से लोड करते समय, AssetBundle को केवल फ़ाइल हेडर लोड करना चाहिए, और इसलिए इसे मेमोरी में थोड़ा कब्जा करना चाहिए।
हालाँकि, यह मामला नहीं था। लोड किए गए AssetBundle को उसकी सभी सामग्रियों के साथ मेमोरी में संग्रहीत किया जाता है। इस प्रकार, एक बनावट को 200 से लोड करने के लिए, एकता सभी 200 बनावटों को मेमोरी में लोड करेगी, एक को ले जाएगी, और फिर शेष 199 बनावटों के लिए मेमोरी खाली कर देगी। डिवाइस पर मेमोरी को मापकर हमने इसे प्रयोगात्मक रूप से पाया।
जाहिर है, मोबाइल उपकरणों के लिए यह स्वीकार्य नहीं है।
सारांश
दिया गया विकल्प एकमात्र तरीका है जिसे हमने डीएलसी और संसाधनों से सिंक्रोनस लोडिंग को लागू करने के लिए पाया है।
एक असम्पीडित AsssetBundle की आवश्यकता होती है, जिसके परिणामस्वरूप DLC लोड करते समय समय और ट्रैफ़िक का बड़ा नुकसान होता है।
विकल्प अपेक्षाकृत छोटे एसेटबंडल्स के लिए उपयुक्त है, जैसा कि बहुत सारी रैम की खपत करता है।
बग पर काम करना (विकल्प 2)
आइए पिछली सभी समस्याओं को ध्यान में रखते हुए और उनके लिए समाधान खोजने की कोशिश करें।
बड़ी संपत्ति लोड करने में समस्या दो तरीकों से हल की जा सकती है।
सबसे पहले WebClient क्लास का उपयोग करना है। हालांकि, आईओएस पर हमें इसके साथ समस्या थी। WebClient कुछ भी डाउनलोड नहीं कर सका, लेकिन यह डेस्कटॉप पर पूरी तरह से काम करता है।
दूसरा विकल्प देशी ओएस कार्यों का उपयोग करना है। उदाहरण के लिए, क्रमशः Android के लिए iOS और URLConnection के लिए NSURLConnection, जो बफ़र को सीधे डिस्क पर फ़ाइल में लोड करने का समर्थन करता है।
लेकिन यह इतनी बड़ी समस्या नहीं है, क्योंकि किसी भी मामले में, हमें तुल्यकालिक लोडिंग के लिए एसेटबंडल के आकार को कम करने की आवश्यकता है। इसलिए, अब, हमने सर्वर से बंडल डाउनलोड करने की वर्तमान विधि को छोड़ दिया है।
बहुत अधिक गंभीर समस्या एसेटबंडल के समकालिक लोडिंग की है। क्योंकि यह न केवल असम्पीडित होना चाहिए, बल्कि थोड़ी मेमोरी स्पेस भी लेना चाहिए, हमें किसी तरह अपनी एक बड़ी डीएलसी फाइल को कई छोटी फाइलों में विभाजित करना चाहिए। हालांकि, अगर हम इसे बहुत छोटी फ़ाइलों में विभाजित करते हैं, तो उनमें से कई होंगे और यह लोड समय को बहुत बढ़ा देगा, क्योंकि आपको प्रत्येक फ़ाइल के लिए फिर से कनेक्ट करना होगा। इसलिए, हमें अभी भी लोड समय और ट्रैफ़िक को बेहतर ढंग से सहेजने के लिए उन्हें संकुचित रखना है।
इस समस्या को हल करने के लिए, अपने स्वयं के अभिलेखागार का उपयोग करने का निर्णय लिया गया। C # के लिए एक ओपन आर्काइव लाइब्रेरी का चुनाव किया गया, जो बिना अधिक प्रयास के, मोनो में यूनिटी के तहत बनाया गया।
इसके अलावा, क्रियाओं का एल्गोरिथ्म इस प्रकार था:
- बंडल बनाते समय, BuildOptions.UncompressedAssetBundle विकल्प को एक असम्पीडित बंडल प्राप्त करने के लिए निर्दिष्ट किया गया था।
- फिर बंडल को संग्रहीत किया गया और अभिलेखागार द्वारा एन्क्रिप्ट किया गया और सर्वर पर अपलोड किया गया।
- जब अनुप्रयोग चल रहा था, तो एक अलग स्ट्रीम बनाई गई थी, जो पृष्ठभूमि में बंडलों को पंप करती थी, उन्हें अनपैक करती थी और उन्हें एक विशेष फ़ोल्डर में डालती थी।
यहाँ हमें एक और समस्या थी। क्योंकि अब हम संग्रहकर्ता द्वारा बंडल बंडल का उपयोग करते हैं, हम अब इसे LoadFromCacheOrDownload फ़ंक्शन के साथ डिफ्लेक्ट नहीं कर सकते हैं। इसलिए, अब हमें डीएलसी के लिए अपने स्वयं के संस्करण नियंत्रण प्रणाली को परिभाषित करना होगा।
डीएलसी संस्करण नियंत्रण प्रणाली के लिए, निम्नलिखित समाधान चुना गया था। सर्वर पर, उस फ़ोल्डर में जहां डीएलसी फाइलें स्थित थीं, उन्होंने एक dlcversion टेक्स्ट फ़ाइल शुरू की। इसमें फ़ोल्डर में DLC की सूची और उनके लिए md5 हैश शामिल था। इन हैश को सर्वर के लिए डीएलसी एप्लेट के चरण में माना जाता था। क्लाइंट के पास ठीक उसी फ़ाइल थी, और जब अनुप्रयोग शुरू हुआ, क्लाइंट ने सर्वर पर फ़ाइल के साथ अपनी फ़ाइल की तुलना की। यदि किसी भी DLC फ़ाइल में उत्कृष्ट हैश था या उसके पास कोई हैश नहीं था, तो यह माना जाता था कि क्लाइंट पर फ़ाइल पुराना था और क्लाइंट ने सर्वर से एक नई DLC फ़ाइल खींची थी।
नई DLC फ़ाइल को डाउनलोड करने और अनपैक किए जाने के बाद, उसके हैश को एक बार फिर सर्वर एक के साथ चेक किया गया, और उसके बाद ही अप्रचलित फ़ाइल को एक नए के साथ बदल दिया गया और क्लाइंट dlcversion फ़ाइल में संबंधित प्रविष्टि बनाई गई।
वर्णित प्रणाली सफलतापूर्वक लागू की गई और ठीक काम करती है। डाउनलोड करने और बैकग्राउंड में DLC को अनपैक करते समय हमारे पास एकमात्र दोष छोटी एफपीएस ड्रॉडाउन (लैग्स) था। एप्लिकेशन मेमोरी खपत के शिखर मूल्य भी थोड़ा बढ़ गए।
आपका ध्यान देने के लिए धन्यवाद। मुझे आपके सवालों के जवाब देने में खुशी होगी।