20 मिलियन टाइल के साथ मानचित्र गेम को स्मृति से बाहर चलाता है, मैं इसे कैसे बचा सकता हूं?


11

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

UPD: यहाँ सवाल यह है कि जब यह अभी भी मुफ्त मेमोरी का उपयोग करने के लिए है तो गेम को क्रैश करना बंद कर दें। चंक्स में बंटवारे के नक्शे के रूप में, यह एक अच्छा तरीका है, लेकिन ऐसा नहीं है जो मैं अपने मामले में चाहता हूं।

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

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


8
केवल खिलाड़ियों के आसपास के क्षेत्रों को लोड करें।
MichaelHouse

4
4 बाइट्स प्रति टाइल पर 20 मिलियन टाइलें लगभग 80 एमबी है - इसलिए ऐसा लगता है कि आपकी टाइलें इतनी छोटी नहीं हैं। हम जादुई रूप से आपको अधिक स्मृति नहीं दे सकते हैं, इसलिए हमें अपने डेटा को छोटा बनाने के लिए काम करना होगा। तो, हमें दिखाएं कि इन टाइलों में क्या है।
काइलोटन

लोडिंग विधि क्या करती है? पोस्ट कोड, क्या आप सामग्री पाइपलाइन का उपयोग करते हैं? क्या यह स्मृति या सिर्फ मेटाडेटा के लिए बनावट लोड करता है? क्या मेटाडेटा? XNA सामग्री प्रकार आम तौर पर अप्रबंधित DirectX सामान को संदर्भित करते हैं ताकि प्रबंधित ऑब्जेक्ट बहुत छोटा हो, लेकिन अप्रबंधित पक्ष पर आपके द्वारा देखे जाने वाले सामान का भार तब तक हो सकता है जब तक आप एक DirectX प्रोफाइलर नहीं चला रहे हों। stackoverflow.com/questions/3050188/…
Oskar Duveborn

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

3
क्या आपको लगता है कि आपके पास मुफ्त मेमोरी है? क्या आप 64-बिट OS के अंदर 32-बिट प्रक्रिया चला रहे हैं?
Dalin Seivewright

जवाबों:


58

यह 1.5Gb तक पहुंचने पर ऐप क्रैश हो जाता है।

यह दृढ़ता से सुझाव देता है कि आप अपनी टाइलों का सही ढंग से प्रतिनिधित्व नहीं कर रहे हैं, क्योंकि इसका मतलब होगा कि प्रत्येक टाइल आकार में ~ 80 बाइट्स है।

आपको यह समझने की आवश्यकता है कि टाइल की गेमप्ले अवधारणा और उपयोगकर्ता द्वारा देखे जाने वाले दृश्य टाइल के बीच एक अलगाव होने की आवश्यकता है। ये दोनों अवधारणाएं एक ही चीज नहीं हैं ।

उदाहरण के लिए टेरारिया को लें। सबसे छोटी टेरारिया दुनिया में 4200x1200 टाइलें हैं, जो 5 मिलियन टाइलें हैं। अब, उस दुनिया का प्रतिनिधित्व करने में कितनी स्मृति लगती है ?

खैर, प्रत्येक टाइल में एक अग्रभूमि परत, एक पृष्ठभूमि परत (पृष्ठभूमि की दीवारें), एक "तार की परत" होती है जहां तार जाते हैं, और एक "फर्नीचर परत" जहां फर्नीचर आइटम जाते हैं। प्रत्येक टाइल कितनी मेमोरी लेती है? फिर, हम सिर्फ वैचारिक रूप से बात कर रहे हैं, न कि नेत्रहीन।

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

प्रति टाइल कुल स्मृति आकार: 2 बाइट्स + 1 बाइट + 1 बिट + 1 बाइट: 4 बाइट्स + 1 बिट। इस प्रकार, एक छोटे से टेरारिया मानचित्र के लिए कुल आकार 20790000 बाइट्स, या ~ 20 एमबी है। (ध्यान दें: ये गणना टेरारिया 1.1 पर आधारित हैं। तब से खेल का बहुत विस्तार हुआ है, लेकिन आधुनिक टेरारिया भी प्रति टाइल स्थान 8 बाइट्स में फिट हो सकता है, या ~ 40 एमबी। अभी भी काफी सहन करने योग्य है)।

आपके पास यह प्रतिनिधित्व कभी भी C # कक्षाओं के सरणियों के रूप में संग्रहीत नहीं होना चाहिए । उन्हें पूर्णांक या कुछ इसी तरह के एरेज़ होने चाहिए। एसी # स्ट्रक्चर भी काम करेगा।

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

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

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

मैं कम से कम सर्वर ऐप (और यदि संभव हो तो क्लाइंट) पर पूरे मानचित्र को संसाधित करना चाहता हूं।

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


4
+1 उत्कृष्ट उत्तर। खदानों से अधिक पर मतदान करने की आवश्यकता है।
MichaelHouse

22

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

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

आप इस तरह के और अधिक विचार पा सकते हैं:

वे इसे कैसे करेंगे: टेरारिया में लाखों टाइलें

एक बड़े टाइल मानचित्र पर क्षेत्रों को 'ज़ोनिंग' करने के साथ-साथ काल कोठरी भी


समस्या ऐसी है जो क्लाइंट ऐप के लिए अच्छा है, लेकिन सर्वर के लिए उतना अच्छा नहीं है। जब दुनिया में कुछ टाइलें बंद हो जाती हैं और एक चेन रिएक्शन शुरू होता है (और यह बहुत कुछ होता है) तो पूरा मैप उसके लिए मेमोरी में होना चाहिए और जब वह मेमोरी से बाहर फेंकता है तो यह एक समस्या है।
user1306322

6
प्रत्येक टाइल में क्या होता है? प्रति टाइल कितनी मेमोरी का उपयोग किया जाता है? यहां तक ​​कि 10 बाइट्स में से प्रत्येक आप केवल 190 मेगाबाइट पर हैं। सर्वर को बनावट या किसी भी अन्य जानकारी को लोड नहीं करना चाहिए, जिसकी आवश्यकता नहीं है। यदि आपको 20 मिलियन टाइलों के लिए एक सिमुलेशन की आवश्यकता है, तो आपको उन टाइलों से जितना संभव हो उतना दूर पट्टी करने की आवश्यकता है एक सिमुलेशन सेट बनाने के लिए जो केवल आपके श्रृंखला प्रतिक्रियाओं के लिए आवश्यक जानकारी है।
MichaelHouse

5

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

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

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

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


3

एक XNA खेल में मैंने जिस कुशल तरीके का उपयोग किया है वह था:

  • स्प्राइटशीट का उपयोग करें, प्रत्येक टाइल / वस्तु के लिए एक छवि नहीं, और बस उस नक्शे पर आपको क्या लोड करना है;
  • मानचित्र को छोटे भागों में विभाजित करें, 500 x 200 टाइलों के मानचित्रों को काटें यदि आपका नक्शा इस आकार का 4 गुना है, या ऐसा कुछ है जिसे आप हल कर सकते हैं;
  • इस चंक को मेमोरी में लोड करें और ऑफस्क्रीन बफर में प्रत्येक दिशा के लिए नक्शे के दृश्य भागों (जैसे, दृश्यमान टाइल प्लस 1 टाइल) को पेंट करें;
  • व्यूपोर्ट को साफ़ करें और पिक्सेल को बफर के ऊपर से कॉपी करें (इसे डबल बफर कहा जाता है और मूव को सुचारू करता है);
  • जब आप चलते हैं, तो नक्शे के गुलदस्ते को ट्रैक करें और अगले चंक को लोड करें जब यह इसके पास हो जाता है और वर्तमान को जोड़ता है;
  • एक बार खिलाड़ी पूरी तरह से इस नए नक्शे में है, तो आप पिछले एक को अनलोड कर सकते हैं।

आप इस तरह से एक भी बड़े मानचित्र का उपयोग कर सकते हैं यदि यह SO बड़ा नहीं है और प्रस्तुत तर्क का उपयोग करें।

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

आपको हर बार इस नक्शे के केवल प्रासंगिक भागों को लूप करना होगा और केवल वही प्रस्तुत करना होगा जो आवश्यक हो। इस तरह आप प्रदर्शन में बहुत सुधार करेंगे।

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

यहाँ एक सलाह यह है कि मैं किसी भी तरह से खेल देव में विशेषज्ञ नहीं हूँ और मेरा खेल मेरे स्नातक की अंतिम परियोजना (जिसमें मेरा सर्वश्रेष्ठ स्कोर था) के लिए बनाया गया था, इसलिए मैं हर मामले में इतना सही नहीं हो सकता, लेकिन मैंने ज्ञान और कौशल को इकट्ठा करने के लिए वेब और http://www.gamasutra.com और XNA रचनाकारों साइट Creators.xna.com (इस समय http://create.msdn.com ) जैसी साइटों पर शोध किया और इसने मेरे लिए ठीक काम किया ।


2

भी

  • अधिक मेमोरी खरीदें
  • अपनी डेटा संरचनाओं का अधिक कॉम्पैक्ट रूप से प्रतिनिधित्व करें
  • एक बार में सभी डेटा मेमोरी में न रखें।

मैं win7 64 बिट पर 8 जीबी रैम है और जब यह 1.5 जीबी तक पहुंच जाता है तो ऐप क्रैश हो जाता है। जब तक मैं कुछ याद नहीं कर रहा हूँ तो वास्तव में अधिक राम खरीदने का कोई मतलब नहीं है।
user1306322

1
@ user1306322: यदि आपके पास 20 मिलियन टाइलें हैं, और इसमें लगभग ~ 1.5GB मेमोरी लगती है, तो इसका मतलब है कि आपकी टाइलें लगभग 80 बाइट्स प्रति टाइल हैं । क्या वास्तव में आप इन चीजों में भंडारण कर रहे हैं?
निकोल बोलस

@ निकोलबोल जैसा कि मैंने कहा, कुछ ints, बाइट्स और एक बनावट 2d। इसके अलावा, वे सभी 1.5GB नहीं लेते हैं, इस मेमोरी कॉस्ट तक पहुंचने पर ऐप क्रैश हो जाता है।
user1306322

2
@ user1306322: यह अभी भी जरूरत से ज्यादा है। कितना बड़ा है texture2d? क्या प्रत्येक टाइल में एक अद्वितीय है, या क्या वे बनावट साझा करते हैं? इसके अलावा, जहां आप यह कहा है? आपने निश्चित रूप से इसे अपने प्रश्न में नहीं डाला है, जो कि जानकारी कहाँ होनी चाहिए। कृपया अपनी विशिष्ट परिस्थितियों को बेहतर ढंग से समझाएं।
निकोल बोलस

@ user1306322: सच कहूं, तो मुझे इस बात का कोई मलाल नहीं है कि मेरा जवाब क्यों नहीं दिया गया। मैंने स्मृति स्थितियों से बाहर के तीन उपचारों की गणना की है - बेशक, इसका मतलब यह नहीं है कि सभी आवश्यक रूप से लागू होते हैं। लेकिन मुझे अभी भी लगता है कि वे सही हैं। मुझे शायद वर्चुअल मशीनों के मामले को शामिल करने के लिए "खरीदने" के बजाय "अपनी मेमोरी का विस्तार" लिखना चाहिए था। क्या मुझे नीचा दिखाया जा रहा है क्योंकि मेरा जवाब क्रिया के बजाय संक्षिप्त था? मुझे लगता है कि वे आगे की व्याख्या के बिना समझा जा करने के लिए पर्याप्त रूप से सीधे आगे हैं ?!
थॉमस

1

यहाँ सवाल यह है कि जब यह अभी भी उपयोग करने के लिए मुफ्त मेमोरी है तो गेम को क्रैश कैसे करना है। चंक्स में बंटवारे के नक्शे के रूप में, यह एक अच्छा तरीका है, लेकिन ऐसा नहीं है जो मैं अपने मामले में चाहता हूं।

आपके अपडेट के जवाब में, आप Int32.MaxValueआकार में सरणियाँ आवंटित नहीं कर सकते । आपको इसे विखंडू में विभाजित करना होगा। आप इसे एक रैपर क्लास में भी एनकैप्सुलेट कर सकते हैं जो एक सरणी-जैसा मुखौटा उजागर करता है:

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