2010-07-05 14 views
19

एक 2 डी गेम के लिए मैं (एंड्रॉइड के लिए) बना रहा हूं, मैं एक घटक-आधारित सिस्टम का उपयोग कर रहा हूं जहां गेमऑब्जेक्ट में कई गेमकंपोनेंट ऑब्जेक्ट्स हैं। गेमकंपोनेंट्स इनपुट घटकों, प्रतिपादन घटकों, बुलेट उत्सर्जक घटकों, आदि जैसे चीजें हो सकती हैं। वर्तमान में, गेमकंपोनेंट्स के पास उस ऑब्जेक्ट का संदर्भ है जो उनका मालिक है और इसे संशोधित कर सकता है, लेकिन GameObject के पास केवल घटकों की एक सूची है और यह तब तक परवाह नहीं करता है जब तक ऑब्जेक्ट अपडेट होने पर उन्हें अपडेट किया जा सके।घटक-आधारित गेम इंजन में संचार

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

मैं इस समस्या को हल करने के कई तरीके के बारे में सोच सकते हैं:

  1. इसके बजाय घटकों की एक पूरी तरह से सामान्य सूची होने के, मैं GameObject महत्वपूर्ण घटकों में से कुछ के लिए विशिष्ट क्षेत्र है दे सकते हैं। उदाहरण के लिए, इसमें एक सदस्य चर हो सकता है जिसे renderingComponent कहा जाता है; जब भी मुझे ऑब्जेक्ट की चौड़ाई प्राप्त करने की आवश्यकता होती है तो मैं बस renderingComponent.getWidth() का उपयोग करता हूं। यह समाधान अभी भी घटकों की सामान्य सूची के लिए अनुमति देता है लेकिन यह उनमें से कुछ को अलग-अलग व्यवहार करता है, और मुझे डर है कि मैं कई असाधारण क्षेत्रों को समाप्त कर दूंगा क्योंकि अधिक घटकों को पूछताछ की आवश्यकता है। कुछ वस्तुओं में प्रतिपादन घटक भी नहीं होते हैं।

  2. गेमऑब्जेक्ट के सदस्यों के रूप में आवश्यक जानकारी है लेकिन घटकों को इसे अपडेट करने की अनुमति दें। इसलिए किसी ऑब्जेक्ट की चौड़ाई और ऊंचाई होती है जो डिफ़ॉल्ट रूप से 0 या -1 होती है, लेकिन एक प्रतिपादन घटक उन्हें अपने अद्यतन लूप में सही मानों पर सेट कर सकता है। यह एक हैक की तरह लगता है और मैं कई चीजों को गेम ऑब्जेक्ट क्लास में सुविधा के लिए धक्का दे सकता हूं, भले ही सभी वस्तुओं को उनकी आवश्यकता न हो।

  3. क्या घटक एक इंटरफेस को कार्यान्वित करते हैं जो इंगित करता है कि किस प्रकार की जानकारी के लिए पूछताछ की जा सकती है। उदाहरण के लिए, एक प्रतिपादन घटक हैस्साइज इंटरफ़ेस को कार्यान्वित करेगा जिसमें GetWidth और getHeight जैसी विधियां शामिल होंगी। जब GameObject को चौड़ाई की आवश्यकता होती है, तो यह अपने घटकों की जांच करता है कि क्या वे हैस्साइज इंटरफेस को कार्यान्वित करते हैं (जावा में instanceof कीवर्ड का उपयोग करके, या is सी # में)। यह एक अधिक सामान्य समाधान की तरह लगता है, एक नुकसान यह है कि घटक की खोज में कुछ समय लग सकता है (लेकिन फिर, अधिकांश वस्तुओं में केवल 3 या 4 घटक होते हैं)।

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

एक खेल के लिए लघु संस्करण

एक घटक आधारित प्रणाली में, जबकि डिजाइन सामान्य रखने वस्तु के लिए घटकों से जानकारी पारित करने के लिए सबसे अच्छा तरीका क्या है?

उत्तर

15

हमें GameDev.net पर सप्ताह में तीन या चार बार इस प्रश्न पर विविधताएं मिलती हैं (जहां गेमोबजेक्ट को आम तौर पर 'इकाई' कहा जाता है) और अब तक सर्वोत्तम दृष्टिकोण पर कोई सहमति नहीं है। कई अलग-अलग दृष्टिकोण काम करने योग्य दिखाए गए हैं, इसलिए मैं इसके बारे में ज्यादा चिंता नहीं करता।

हालांकि, आमतौर पर घटकों के बीच संचार करने की समस्याएं होती हैं। शायद ही लोग किसी घटक से इकाई को जानकारी प्राप्त करने के बारे में चिंता करते हैं - अगर कोई इकाई जानता है कि उसे किस जानकारी की आवश्यकता है, तो संभवतः यह जानता है कि उसे किस प्रकार के घटक तक पहुंचने की आवश्यकता है और उस घटक या विधि को उस घटक पर कॉल करने की आवश्यकता है आँकड़े। यदि आपको सक्रिय होने के बजाय प्रतिक्रियाशील होने की आवश्यकता है, तो कॉलबैक पंजीकृत करें या घटकों के साथ एक पर्यवेक्षक पैटर्न स्थापित करें ताकि इकाई को पता चल सके कि घटक में कुछ बदल गया है, और उस बिंदु पर मान पढ़ें।

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

संचार के लिए के रूप में:

कुछ लोग मान्यताओं बना रहे हैं कि एक इकाई हमेशा घटक प्रकार (जहां प्रत्येक उदाहरण के लिए कई संभावित उपवर्गों में से एक है) और इसलिए बस के लिए एक सीधा संदर्भ प्राप्त कर सकते हैं का एक ज्ञात सेट में शामिल होंगे अन्य घटक और अपने सार्वजनिक इंटरफ़ेस के माध्यम से पढ़ें/लिखें।

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

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

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

+0

मुझे उम्मीद है कि यह जवाब होगा; कि कई दृष्टिकोण हैं और वे सभी अलग-अलग संदर्भों में काम करने योग्य हैं। आप पूरी तरह जेनेरिक सिस्टम के बारे में सही हैं, मैं अधिक इंजीनियरिंग था और उन समस्याओं के बारे में चिंता कर रहा हूं जो अस्तित्व में नहीं हैं। –

4

"event bus" का उपयोग करें। (ध्यान दें कि आप शायद कोड का उपयोग नहीं कर सकते हैं लेकिन यह आपको मूल विचार देना चाहिए)।

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

[संपादित करें] अधिक विस्तृत चर्चा के लिए, message passing देखें (snk_kid पर इसका संकेत देने के लिए धन्यवाद)।

+1

संदेश-पासिंग आर्किटेक्चर की तरह लगता है, जैसा कि इसे आम तौर पर कहा जाता है। –

+0

यह शायद सबसे सामान्य दृष्टिकोण है। मेरे जैसे छोटे खेल के लिए शीर्ष पर थोड़ा सा, लेकिन एक सामान्य समाधान के रूप में विचार करने लायक है। –

+0

ठीक है, यह डिजाइन को बहुत सरल बनाता है क्योंकि यह इतना सामान्य और कार्यान्वित करने में आसान है (जावा में कोड की केवल 50 लाइनें, पाइथन में 20)। जब आपका गेम काम करता है, तो आप अभी भी कुछ बिट्स को अनुकूलित कर सकते हैं लेकिन यह आपको ऊपर ले जाता है और जल्दी से चल रहा है। –

3

एक दृष्टिकोण घटकों के एक कंटेनर को शुरू करना है। प्रत्येक घटक एक सेवा प्रदान कर सकता है और अन्य घटकों से सेवाओं की भी आवश्यकता हो सकती है। आपकी प्रोग्रामिंग भाषा और पर्यावरण के आधार पर आपको यह जानकारी प्रदान करने के लिए एक विधि के साथ आना होगा।

अपने सबसे सरल रूप में आपके घटकों के बीच एक-एक कनेक्शन हैं, लेकिन आपको एक से कई कनेक्शन की भी आवश्यकता होगी। जैसे CollectionDetector में IBoundingBox लागू करने वाले घटकों की एक सूची होगी।

प्रारंभ के दौरान कंटेनर घटकों के बीच कनेक्शन को तार देगा, और रन-टाइम के दौरान कोई अतिरिक्त लागत नहीं होगी।

यह आपके समाधान के करीब है 3), उम्मीद है कि घटकों के बीच कनेक्शन केवल एक बार वायर्ड होते हैं और गेम लूप के हर पुनरावृत्ति पर चेक नहीं किए जाते हैं।

Managed Extensibility Framework for .NET इस समस्या का एक अच्छा समाधान है। मुझे एहसास है कि आप एंड्रॉइड पर विकसित करना चाहते हैं, लेकिन आपको अभी भी इस ढांचे से कुछ प्रेरणा मिल सकती है।

+0

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

10

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

अपने विशिष्ट परिदृश्य जहां कहीं सीमांकन बॉक्स स्टोर और केवल टक्कर घटक की जरूरत के लिए इसके बारे में परवाह है, मैं करूंगा:

  1. स्टोर यह टक्कर घटक अपने आप में।
  2. टक्कर पहचान कोड सीधे घटकों के साथ काम करें।

तो, टकराव इंजन को बातचीत के समाधान के लिए GameObjects के संग्रह के माध्यम से पुनरावृत्ति करने के बजाय, इसे सीधे CollisionComponents के संग्रह के माध्यम से पुन: सक्रिय करें। टक्कर हो जाने के बाद, यह उसके मूल गेमऑब्जेक्ट को धक्का देने के लिए घटक पर निर्भर करेगा।

  1. GameObject से बाहर टक्कर विशेष राज्य छोड़ देता है:

    यह आपको लाभ की एक जोड़ी देता है।

  2. आपको गेमऑब्जेक्ट्स पर फिर से चलाने से बचाता है जिसमें टकराव घटक नहीं होते हैं। (यदि आपके पास दृश्य प्रभाव और सजावट जैसी बहुत सी गैर-संवादात्मक वस्तुएं हैं, तो यह चक्रों की एक सभ्य संख्या को बचा सकता है।)
  3. ऑब्जेक्ट और उसके घटक के बीच चलने वाले चक्रों को जलाने से बचाता है। यदि आप ऑब्जेक्ट्स के माध्यम से पुन: प्रयास करते हैं तो प्रत्येक पर getCollisionComponent() करें, जो पॉइंटर-निम्न कैश मिस का कारण बन सकता है। ऐसा करना कि प्रत्येक वस्तु के लिए प्रत्येक फ्रेम के लिए बहुत सी सीपीयू जला सकती है।

यदि आप रुचि रखते हैं तो मेरे पास इस पैटर्न here पर अधिक है, हालांकि ऐसा लगता है कि आप उस अध्याय में जो कुछ भी पहले से ही समझ चुके हैं।

+0

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

+0

हाँ, मुझे पता है! पुस्तक दुर्भाग्यवश थोड़ी देर के लिए अंतराल पर है। मैंने हाल ही में गेम उद्योग छोड़ दिया और पूरे देश में चले गए, इसलिए मुझे अपने दिमाग में अन्य सामान मिल गया है। उम्मीद है कि, मैं जल्द ही इसे वापस मिल जाऊंगा। – munificent

+1

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

संबंधित मुद्दे