कैसे एक AssetManager डिजाइन करने के लिए?


26

एक AssestManager को डिजाइन करने के लिए सबसे अच्छा तरीका क्या है जो गेम के ग्राफिक्स, ध्वनियों आदि का संदर्भ देगा?

क्या इन परिसंपत्तियों को एक कुंजी / मूल्य मानचित्र जोड़ी में संग्रहीत किया जाना चाहिए? यानी मैं "पृष्ठभूमि" संपत्ति के लिए पूछता हूं और मैप संबंधित बिटमैप को वापस करता है? वहाँ भी एक बेहतर तरीका है?

विशेष रूप से मैं एक एंड्रॉइड / जावा गेम लिख रहा हूं, लेकिन उत्तर सामान्य हो सकते हैं।

जवाबों:


16

यह आपके खेल के दायरे पर निर्भर करता है। एक परिसंपत्ति प्रबंधक बड़े खिताब के लिए बिल्कुल आवश्यक है, छोटे खेलों के लिए कम।

बड़े शीर्षकों के लिए आपको निम्नलिखित जैसी समस्याओं का प्रबंधन करना होगा:

  • साझा की गई संपत्ति - क्या ईंट की बनावट कई मॉडलों द्वारा उपयोग की जा रही है?
  • संपत्ति का जीवनकाल - क्या वह संपत्ति जिसे आपने 15 मिनट पहले लोड किया था, उसकी अब आवश्यकता नहीं है? संदर्भ यह सुनिश्चित करने के लिए कि आपके पास कुछ है आदि के साथ समाप्त होने पर अपनी संपत्ति की गिनती करें
  • DirectX 9 में यदि कुछ विशेष प्रकार के लोड किए गए हैं और आपका ग्राफिक्स डिवाइस 'खो गया' है (ऐसा तब होता है जब आप Ctrl + Alt + Del को अन्य चीजों के बीच दबाते हैं) - आपके गेम को उन्हें फिर से बनाने की आवश्यकता होगी
  • जरूरत पड़ने पर अग्रिम संपत्ति लोड करना - आप इसके बिना बड़े खुले विश्व खेल नहीं बना सकते
  • बल्क लोडिंग एसेट्स - हम अक्सर लोडिंग टाइम को बेहतर बनाने के लिए बहुत सारी एसेट्स को एक ही फाइल में पैक करते हैं - डिस्क के आसपास की मांग में बहुत समय लगता है

छोटे शीर्षकों के लिए ये चीजें एक मुद्दे से कम हैं, XNA जैसे ढांचे उनके भीतर परिसंपत्ति प्रबंधक हैं - इसे फिर से आविष्कार करने के लिए बहुत कम बिंदु हैं।

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

आमतौर पर आपके ऐप में हार्डकोड फाइलनामों की सलाह नहीं दी जाती है, आमतौर पर एक और डेटा फॉर्मेट (जैसे कि xml) को 'आईडी' में फाइलनाम को चित्रित करना बेहतर होता है।

  • एक मनोरंजक पक्ष नोट के रूप में, आप आम तौर पर प्रति परियोजना एक हैश टक्कर प्राप्त करते हैं।

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

2
@Joe Wreschnig - आप किसी ऐसेट मैनेजर का उपयोग किए बिना icStatic द्वारा उल्लिखित पांच आवश्यकताओं को कैसे संबोधित करेंगे?
एंटीइनोम

8

("एक परिसंपत्ति प्रबंधक का उपयोग न करने" से बचने की कोशिश कर रहा हूँ -खुद को यहाँ, क्योंकि मैं इसे अपमानजनक मानता हूं)।

एक कुंजी / मूल्य नक्शा एक बहुत ही उपयोगी दृष्टिकोण है।

हमारे पास एक संसाधन प्रबंधन कार्यान्वयन है जहां विभिन्न संसाधन प्रकारों के लिए इकाइयां पंजीकरण कर सकती हैं।

"GetResource" विधि वांछित resourcetype के लिए सही फ़ैक्टरी खोजने के लिए टेम्प्लेट का उपयोग करती है और एक विशिष्ट रिसोर्सहैंडल (फिर से SpecificResourceHandle को वापस करने के लिए टेम्प्लेट का उपयोग करके) लौटाती है।

रिसोर्स मैनजर (रिसोर्सहैंडल के अंदर) द्वारा संसाधनों को रिफंड किया जाता है और जब उन्हें जरूरत नहीं होती है तब जारी किया जाता है।

पहला एडऑन हमने लिखा था "रीलोड (XYZ)" विधि, जो हमें किसी भी कोड को बदलने या गेम को फिर से लोड किए बिना रनिंग इंजन के बाहर से संसाधनों को बदलने की अनुमति देती है। (यह आवश्यक है जब कलाकार कंसोल पर काम करते हैं?))

ज्यादातर समय हमारे पास केवल संसाधन प्रबंधक के उदाहरण पर होता है, लेकिन कभी-कभी हम एक स्तर या मानचित्र के लिए एक नया उदाहरण बनाते हैं। इस तरह हम सिर्फ levelResourceManager पर "शटडाउन" कह सकते हैं और यह सुनिश्चित कर सकते हैं कि कुछ भी लीक न हो।

(संक्षिप्त) उदाहरण

// very abbreviated!
// this code would never survive our coding guidelines ;)

ResourceManager* pRm = new ResourceManager;
pRm->initialize( );
pRm->registerFactory( new TextureFactory );
// [...]
TextureHandle tex = pRm->getResource<Texture>( "test.otx" ); // in real code we use some macro magic here to use CRCs for filenames
tex->storeToHardware( 0 ); // channel 0

pRm->releaseResource( pRm );

// [...]
pRm->shutdown(); // will log any leaked resource

6

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

स्टीव Yegge (कई, कई अन्य लोगों के बीच) ने एक अच्छी कहानी लिखी है कि कैसे एकल प्रबंधक पैटर्न, सिंगलटन पैटर्न के माध्यम से, अंत में होते हैं। http://sites.google.com/site/steveyegge2/singleton-considered-stupid


2
पक्का निश्चित। लेकिन एंड्रॉइड (या अन्य गेम) जैसे मामलों में आपको गेम शुरू करने से पहले बहुत सारे ग्राफिक्स / ध्वनियों को मेमोरी में लोड करना होगा, उस दौरान नहीं। लोड स्क्रीन के दौरान मैं ऐसा कैसे कर सकता हूं जो आप (कारखानों) कह रहे हैं? बस लोड स्क्रीन पर कारखाने में हर वस्तु को मारा तो यह उन्हें कैश करता है?
ब्रायन डेनी

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

@Joe "लोडिंग स्क्रीन्स" के बारे में मेरे अन्य प्रश्न पर एक नज़र डालें: gamedev.stackexchange.com/questions/1171/… डिस्क पर जाने के लिए खाली कैश रखने का लंबे समय का मतलब है और उन पहली कॉल के दौरान कुछ एफपीएस प्रदर्शन हिट हो सकता है। । यदि आप पहले से ही जानते हैं कि आप समय से पहले हिट करने जा रहे हैं, तो क्या इसे लोड करने के दौरान इसे प्री-कैश कर सकते हैं, है ना?
ब्रायन डेनी

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

1
@ जो, एक कारखाना भी "समर्पित प्रबंधक" नहीं है?
एमएसएन

2

मैंने हमेशा सोचा है कि एक अच्छे एसेट मैनेजर के पास ऑपरेशन के कई तरीके होने चाहिए। इन तरीकों से सबसे अधिक संभावना होगी कि एक सामान्य इंटरफ़ेस का पालन करने वाले अलग-अलग स्रोत मॉड्यूल होंगे। ऑपरेशन के दो बुनियादी तरीके होंगे:

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

आपको एक उपकरण की आवश्यकता होगी जो साझा डेटाबेस से सभी एसेस्ट को पकड़ सके और उत्पादन डेटासेट बना सके।

एक डेवलपर के रूप में मेरे वर्षों में, मैंने कभी ऐसा कुछ नहीं देखा, हालांकि मैंने केवल कुछ मुट्ठी भर कंपनियों के लिए काम किया है, इसलिए मेरा विचार वास्तव में प्रतिनिधि नहीं है।

अद्यतन करें

ठीक है, कुछ नकारात्मक वोट। मैं इस डिज़ाइन पर विस्तार करूँगा।

सबसे पहले, आपको वास्तव में फैक्ट्री कक्षाओं की आवश्यकता नहीं है क्योंकि अगर आपको मिल गया है:

TextureHandle tex = pRm->getResource<Texture>( "test.otx" );

आप प्रकार जानते हैं, इसलिए बस करें:

TextureHandle tex = new TextureHandle ("test.otx");

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

if (texture already loaded)
  update texture reference count
else
  asset_stream = new AssetStream (resource_id)
  asset_stream->ReadBytes
  create texture
  set texture ref count to 1

डेटा का स्थान खोजने के लिए AssetStream रिसोर्स_ड पैरामीटर का उपयोग करता है। जिस तरह से यह किया गया है वह आपके द्वारा चलाए जा रहे पर्यावरण पर निर्भर करेगा:

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

रिलीज़ में: स्ट्रीम एक कुंजी / मान तालिका में ID को एक बड़े, पैक्ड फ़ाइल (जैसे Doom की WAD फ़ाइल) में ऑफसेट / आकार प्राप्त करने के लिए देखती है।


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

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

2

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

मेरा तरीका खुली दुनिया के मानचित्रों के लिए एकदम सही है, लेकिन एक स्तर-आधारित गेम आपको इस सहजता से लाभ नहीं देगा। उम्मीद है की यह मदद करेगा!

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