हालाँकि, क्या यह उचित है कि गेम इंजनों में कॉमनर-एंटिटी-सिस्टम आर्किटेक्चर का उपयोग करके एप्लिकेशन बनाएं?
मेरे लिए, बिल्कुल। मैं विज़ुअल एफएक्स में काम करता हूं और इस क्षेत्र में कई तरह की प्रणालियों का अध्ययन किया, उनके आर्किटेक्चर (सीएडी / सीएएम सहित), एसडीके के लिए भूखे और कोई भी कागजात जो मुझे प्रतीत होता है कि असीम वास्तु निर्णयों के पेशेवरों और विपक्षों की भावना देगा। बनाया जा सकता है, यहां तक कि सबसे सूक्ष्म वाले हमेशा सूक्ष्म प्रभाव नहीं बनाते हैं।
वीएफएक्स बल्कि खेल के समान है जिसमें "दृश्य" की एक केंद्रीय अवधारणा है, जिसमें व्यूपोर्ट प्रदान किए गए परिणामों को प्रदर्शित करते हैं। एनीमेशन संदर्भों में लगातार इस दृश्य के चारों ओर घूमते हुए बहुत सारे केंद्रीय ढलान प्रसंस्करण होते हैं, जहाँ भौतिकी हो रही है, कण उत्सर्जित कर सकते हैं कण, जाल एनिमेटेड और गाया जा रहा है, गति एनिमेशन, आदि, और अंततः उन्हें प्रस्तुत करने के लिए। अंत में उपयोगकर्ता के लिए सभी।
कम से कम बहुत जटिल गेम इंजन के लिए एक और समान अवधारणा एक "डिजाइनर" पहलू की आवश्यकता थी जहां डिजाइनर लचीले ढंग से दृश्यों को डिजाइन कर सकते हैं, जिसमें उनकी खुद की कुछ हल्की प्रोग्रामिंग (स्क्रिप्ट और नोड्स) करने की क्षमता भी शामिल है।
मैंने पाया, पिछले कुछ वर्षों में, ईसीएस ने सबसे अच्छा फिट बनाया। बेशक, यह कभी भी पूरी तरह से व्यक्तिपरकता से तलाक नहीं लेता है, लेकिन मैं कहूंगा कि यह दृढ़ता से सबसे कम समस्याओं को प्रकट करता है। इसने कई और बड़ी समस्याओं को हल कर दिया, जिनसे हम हमेशा जूझ रहे थे, जबकि बदले में हमें कुछ नए नाबालिग दे रहे थे।
पारंपरिक ओओपी
अधिक पारंपरिक OOP दृष्टिकोण वास्तव में मजबूत हो सकते हैं जब आपके पास डिजाइन आवश्यकताओं की मजबूती होती है, लेकिन कार्यान्वयन आवश्यकताओं की नहीं। चाहे चापलूसी कई इंटरफ़ेस दृष्टिकोण या एक अधिक नेस्टेड पदानुक्रमित एबीसी दृष्टिकोण के माध्यम से, यह डिजाइन को सीमेंट करने के लिए जाता है और कार्यान्वयन को आसान बनाने और बदलने के लिए सुरक्षित बनाते समय इसे बदलना अधिक कठिन होता है। किसी एक उत्पाद में हमेशा अस्थिरता की आवश्यकता होती है, जो एकल संस्करण से आगे बढ़ता है, इसलिए ओओपी दृष्टिकोण तिरछी स्थिरता (परिवर्तन की कठिनाई और बदलाव के कारणों में कमी) की ओर जाता है, और अस्थिरता (परिवर्तन में आसानी और परिवर्तन के कारण) कार्यान्वयन स्तर तक।
हालांकि, उपयोगकर्ता के अंत की आवश्यकताओं को विकसित करने के खिलाफ, डिजाइन और कार्यान्वयन दोनों को बार-बार बदलने की आवश्यकता हो सकती है। आपको एक अजीब उपयोगकर्ता की तरह कुछ अजीब लग सकता है जैसे कि एनालॉग जीव के लिए एक मजबूत उपयोगकर्ता की आवश्यकता है जो एक ही समय में पौधे और जानवर दोनों की आवश्यकता होती है, जो आपके द्वारा बनाए गए संपूर्ण वैचारिक मॉडल को पूरी तरह से अमान्य करता है। सामान्य वस्तु-उन्मुख दृष्टिकोण यहां आपकी रक्षा नहीं करते हैं, और कभी-कभी ऐसे अप्रत्याशित, अवधारणा-तोड़ने वाले परिवर्तनों को और भी कठिन बना सकते हैं। जब बहुत प्रदर्शन-महत्वपूर्ण क्षेत्र शामिल होते हैं, तो डिजाइन के कारणों में और अधिक परिवर्तन होता है।
एक वस्तु के अनुरूप इंटरफेस बनाने के लिए कई, दानेदार इंटरफेस के संयोजन से क्लाइंट कोड को स्थिर करने में बहुत मदद मिल सकती है, लेकिन यह उपप्रकारों को स्थिर करने में मदद नहीं करता है जो कभी-कभी ग्राहक निर्भरता की संख्या को बौना कर सकता है। उदाहरण के लिए, आपके सिस्टम के केवल एक भाग के द्वारा एक इंटरफ़ेस का उपयोग किया जा सकता है, लेकिन उस इंटरफ़ेस को लागू करने वाले एक हजार अलग-अलग उपप्रकारों के साथ। उस मामले में, जटिल उपप्रकारों को बनाए रखना (जटिल क्योंकि उनके पास इतनी सारी विषम इंटरफ़ेस जिम्मेदारियों को पूरा करने के लिए है) एक इंटरफ़ेस के माध्यम से उनका उपयोग करने वाले कोड के बजाय दुःस्वप्न बन सकता है। OOP ऑब्जेक्ट स्तर में जटिलता को स्थानांतरित करता है, जबकि ECS इसे क्लाइंट ("सिस्टम") स्तर पर स्थानांतरित करता है, और यह तब आदर्श हो सकता है जब बहुत कम सिस्टम हों लेकिन "ऑब्जेक्ट" ("एंटिटीज") का एक पूरा गुच्छा।
एक वर्ग भी निजी तौर पर अपने डेटा का मालिक है, और इस तरह सभी अपने आप ही आक्रमणकारियों को बनाए रख सकता है। फिर भी, "मोटे" आक्रमणकारी हैं जो वास्तव में अभी भी बनाए रखने के लिए कठिन हो सकते हैं जब ऑब्जेक्ट एक दूसरे के साथ बातचीत करते हैं। एक जटिल प्रणाली के रूप में एक पूरे के लिए एक वैध स्थिति में होने के लिए अक्सर वस्तुओं के एक जटिल ग्राफ पर विचार करने की आवश्यकता होती है, भले ही उनके व्यक्तिगत आक्रमणों को ठीक से बनाए रखा जाए। पारंपरिक ओओपी-शैली दृष्टिकोण दानेदार आक्रमणकारियों को बनाए रखने में मदद कर सकता है, लेकिन वास्तव में व्यापक, मोटे आक्रमणकारियों को बनाए रखना मुश्किल बना सकता है यदि ऑब्जेक्ट सिस्टम के नन्हे पहलुओं पर ध्यान केंद्रित करते हैं।
यहीं पर इस प्रकार के लेगो-ब्लॉक-बिल्डिंग ईसीएस दृष्टिकोण या वेरिएंट इतने सहायक हो सकते हैं। सामान्य ऑब्जेक्ट की तुलना में सिस्टम के मोटे होने के साथ-साथ, सिस्टम के पक्षी-दृष्टि पर उन प्रकार के मोटे आक्रमणकारियों को बनाए रखना आसान हो जाता है। बहुत सारी नन्हा वस्तु अंतःक्रियाएं एक बड़ी प्रणाली में बदल जाती हैं, जो एक छोटी-छोटी वस्तुओं के बजाय एक व्यापक कार्य पर ध्यान केंद्रित करती है, जो एक छोटे आकार के कार्यों पर ध्यान केंद्रित करती है, जो निर्भरता ग्राफ के साथ नन्हे-नन्हे कार्यों पर केंद्रित होता है, जो एक किलोमीटर के दायरे में आते हैं।
फिर भी मुझे ECS के बारे में जानने के लिए, गेमिंग उद्योग में, अपने क्षेत्र से बाहर देखना पड़ा, हालाँकि मैं हमेशा डेटा-उन्मुख था। इसके अलावा, मज़ेदार रूप से, मैंने लगभग अपने दम पर ECS की ओर अपना रास्ता बनाया और बेहतर डिजाइन के साथ आने की कोशिश कर रहा था। मैंने इसे सभी तरह से नहीं बनाया और एक बहुत ही महत्वपूर्ण विवरण याद किया, जो कि "सिस्टम" भाग की औपचारिकता है, और कच्चे डेटा पर सभी तरह से घटकों को स्क्वैश करना है।
मैं इस बात से गुज़रने की कोशिश करूँगा कि मैंने ECS पर किस तरह से समझौता किया, और कैसे पिछले डिज़ाइन पुनरावृत्तियों के साथ सभी समस्याओं को हल किया। मुझे लगता है कि यह ठीक से उजागर करने में मदद करेगा कि यहां क्यों जवाब एक बहुत मजबूत "हां" हो सकता है, कि ईसीएस गेमिंग उद्योग से कहीं अधिक संभावित रूप से लागू है।
1980 का ब्रूट फोर्स आर्किटेक्चर
वीएफएक्स उद्योग में मैंने जिस पहली वास्तुकला पर काम किया था, उसकी एक लंबी विरासत थी जो कंपनी में शामिल होने के एक दशक पहले से ही चल रही थी। यह सभी तरह से क्रूट सी क्रूड सी कोडिंग था (सी पर तिरछा नहीं, जैसा कि मैं सी से प्यार करता हूं, लेकिन जिस तरह से यहां इस्तेमाल किया जा रहा है वह वास्तव में क्रूड था)। एक लघु और अतिस्पर्शी टुकड़ा इस तरह निर्भरता से मिलता-जुलता है:
और यह सिस्टम के एक छोटे टुकड़े का एक बहुत सरल आरेख है। आरेख में इनमें से प्रत्येक ग्राहक ("प्रतिपादन", "भौतिकी", "मोशन") को कुछ "जेनेरिक" वस्तु मिलेगी, जिसके माध्यम से वे एक प्रकार के क्षेत्र की जांच करेंगे, जैसे:
void transform(struct Object* obj, const float mat[16])
{
switch (obj->type)
{
case camera:
// cast to camera and do something with camera fields
break;
case light:
// cast to light and do something with light fields
break;
...
}
}
बेशक बहुत बदसूरत और इससे अधिक जटिल कोड के साथ। इन स्विच मामलों से अक्सर अतिरिक्त कार्यों को बुलाया जाएगा जो बार-बार स्विच को फिर से करेगा। यह आरेख और कोड लगभग ईसीएस-लाइट की तरह लग सकता है, लेकिन इसमें कोई मजबूत इकाई-घटक अंतर नहीं था (" क्या यह ऑब्जेक्ट एक कैमरा है?", "यह ऑब्जेक्ट गति प्रदान नहीं करता है?"), और "सिस्टम" की कोई औपचारिकता नहीं है। बस नेस्टेड फ़ंक्शन का एक गुच्छा सभी जगह जा रहा है और जिम्मेदारियों को मिला रहा है)। उस मामले में, बस के बारे में सब कुछ जटिल था, किसी भी समारोह में एक आपदा होने की प्रतीक्षा करने की क्षमता थी।
हमारे यहाँ परीक्षण प्रक्रिया में अक्सर चीज़ों की जाँच करना पड़ता था जैसे कि अन्य प्रकार की वस्तुओं से अलग जाली होती है, भले ही दोनों समान चीज़ हो रही हो, क्योंकि यहाँ कोडिंग की क्रूर बल प्रकृति (अक्सर बहुत कॉपी और पेस्ट के साथ) अक्सर बनाई जाती है यह बहुत संभावित है कि अन्यथा वही तर्क एक आइटम प्रकार से अगले तक विफल हो सकता है। नए प्रकार के आइटमों को संभालने के लिए सिस्टम को विस्तारित करने की कोशिश करना बहुत निराशाजनक था, भले ही एक दृढ़ता से व्यक्त उपयोगकर्ता-अंत की आवश्यकता थी, क्योंकि यह बहुत मुश्किल था जब हम मौजूदा प्रकार की वस्तुओं को संभालने के लिए बहुत संघर्ष कर रहे थे।
कुछ पेशेवरों:
- उह ... इंजीनियरिंग का कोई अनुभव नहीं है, मुझे लगता है? इस प्रणाली को बहुरूपता जैसी बुनियादी अवधारणाओं के किसी भी ज्ञान की आवश्यकता नहीं है, यह पूरी तरह से क्रूर बल है, इसलिए मुझे लगता है कि एक शुरुआत भी कोड में से कुछ को समझने में सक्षम हो सकती है, भले ही डिबगिंग में एक प्रो मुश्किल से इसे बनाए रख सके।
कुछ विपक्ष:
- रख-रखाव बुरा सपना। हमारी मार्केटिंग टीम ने वास्तव में हमें एक 3 साल के चक्र में 2000 से अधिक अद्वितीय बग्स को निर्धारित करने की आवश्यकता महसूस की। मेरे लिए यह कुछ शर्मिंदा करने वाली बात है कि हमारे पास पहली बार में इतने सारे कीड़े थे, और यह प्रक्रिया शायद अभी भी लगभग 10% कीड़े ही तय की गई थी जो हर समय संख्या में बढ़ रहे थे।
- सबसे अधिक संभव समाधान के बारे में।
1990 के दशक का COM आर्किटेक्चर
वीएफएक्स उद्योग के अधिकांश लोग वास्तुकला की इस शैली का उपयोग करते हैं, जो मैंने इकट्ठा किया है, उनके डिजाइन निर्णयों के बारे में दस्तावेजों को पढ़ रहा है और उनके सॉफ्टवेयर विकास किट पर नज़र रखता है।
यह बिल्कुल एबीआई स्तर पर कॉम नहीं हो सकता है (इनमें से कुछ आर्किटेक्चर में केवल एक ही कंपाइलर का उपयोग करके लिखे गए प्लगइन्स हो सकते हैं), लेकिन इंटरफ़ेस पर पूछे गए इंटरफ़ेस प्रश्नों के साथ बहुत सी समान विशेषताओं को साझा करता है यह देखने के लिए कि उनके घटकों का समर्थन क्या होता है।
इस तरह के दृष्टिकोण के साथ, transform
ऊपर दिए गए एनालॉग फंक्शन इस फॉर्म से मिलते जुलते थे:
void transform(Object obj, const Matrix& mat)
{
// Wrapper that performs an interface query to see if the
// object implements the IMotion interface.
MotionRef motion(obj);
// If the object supported the IMotion interface:
if (motion.valid())
{
// Transform the item through the IMotion interface.
motion->transform(mat);
...
}
}
यह उस पुराने कोडबेस की नई टीम का दृष्टिकोण है, जो अंततः की ओर फिर से सक्रिय हो जाता है। और यह लचीलेपन और स्थिरता के मामले में मूल पर एक नाटकीय सुधार था, लेकिन अभी भी कुछ मुद्दे थे जिन्हें मैं अगले भाग में कवर करूंगा।
कुछ पेशेवरों:
- पिछले जानवर बल समाधान की तुलना में नाटकीय रूप से अधिक लचीला / एक्स्टेंसिबल / बनाए रखने योग्य है।
- हर इंटरफ़ेस को पूरी तरह से अमूर्त (स्टेटलेस, नो इम्प्लीमेंटेशन, केवल प्योर इंटरफेस) बनाकर एसओएलआईडी के कई सिद्धांतों के लिए एक मजबूत अनुरूपता को बढ़ावा देता है।
कुछ विपक्ष:
- बॉयलरप्लेट के बहुत सारे। हमारे घटकों को वस्तुओं को त्वरित करने के लिए एक रजिस्ट्री के माध्यम से प्रकाशित किया जाना था, इंटरफेस में उन्होंने दोनों को विरासत में ("कार्यान्वयन") इंटरफेस की आवश्यकता का समर्थन किया था और यह बताने के लिए कि कौन सी इंटरफेस एक क्वेरी में उपलब्ध थे, कुछ कोड प्रदान करते हैं।
- शुद्ध इंटरफेस के परिणामस्वरूप पूरे स्थान पर दोहराए गए तर्क को बढ़ावा दिया। उदाहरण के लिए, लागू होने वाले सभी घटकों में
IMotion
हमेशा एक ही स्थिति और सभी कार्यों के लिए सटीक एक ही कार्यान्वयन होगा। इसे कम करने के लिए, हम उन चीजों के लिए पूरे सिस्टम में बेस क्लासेस और हेल्पर फंक्शनलिटी को सेंट्रलाइज़ करना शुरू कर देंगे, जो एक ही इंटरफेस के लिए एक ही तरह से बेमानी तरीके से लागू होंगे, और संभवतः कई इनहेरिटेंस के साथ हुड के पीछे जा रहे हैं, लेकिन यह सुंदर था ग्राहक कोड आसान था, भले ही हुड के नीचे गड़बड़।
- अक्षमता: vtune सत्र अक्सर मूल
QueryInterface
फ़ंक्शन को लगभग हमेशा ऊपरी हॉटस्पॉट के मध्य के रूप में दिखाते हैं, और कभी-कभी # 1 हॉटस्पॉट भी दिखाते हैं। इसे कम करने के लिए, हम उन चीजों को करेंगे जैसे कोडबेस कैश के कुछ हिस्सों को प्रदान करने के लिए पहले से ही ज्ञात वस्तुओं की एक सूची हैIRenderable
, लेकिन इससे जटिलता और रखरखाव की लागत में काफी वृद्धि हुई। इसी तरह, यह मापना अधिक कठिन था, लेकिन हमने सी-स्टाइल कोडिंग की तुलना में कुछ निश्चित मंदी देखी, जो हम पहले कर रहे थे जब हर एक इंटरफ़ेस को डायनेमिक प्रेषण की आवश्यकता थी। ब्रांच मिसप्रिंट और ऑप्टिमाइज़ेशन बैरियर जैसी चीजें कोड के थोड़े से पहलू के बाहर मापना मुश्किल है, लेकिन उपयोगकर्ता आमतौर पर यूजर इंटरफेस की प्रतिक्रिया को नोटिस कर रहे थे और इस तरह की चीजें सॉफ्टवेयर साइड-बाय के पिछले और नए संस्करणों की तुलना में खराब हो रही थीं- उन क्षेत्रों के लिए जहां एल्गोरिदम की जटिलता नहीं बदली, केवल स्थिरांक।
- व्यापक प्रणाली स्तर पर शुद्धता के बारे में तर्क करना अभी भी मुश्किल था। हालांकि यह पिछले दृष्टिकोण की तुलना में काफी आसान था, फिर भी इस पूरे सिस्टम में वस्तुओं के बीच जटिल बातचीत को समझना मुश्किल था, विशेष रूप से कुछ अनुकूलन के साथ जो इसके खिलाफ आवश्यक होने लगे।
- हमें अपने इंटरफेस को सही करने में परेशानी हुई। भले ही सिस्टम में केवल एक व्यापक स्थान हो सकता है जो एक इंटरफ़ेस का उपयोग करता है, उपयोगकर्ता-एंड की आवश्यकताएं संस्करणों पर बदल जाएंगी, और हम अंत में सभी वर्गों के लिए कैस्केडिंग परिवर्तन करना चाहेंगे जो एक नया फ़ंक्शन समायोजित करने के लिए इंटरफ़ेस को लागू करते हैं। इंटरफ़ेस, उदाहरण के लिए, जब तक कि कुछ सार आधार वर्ग नहीं था जो पहले से ही हुड के तहत तर्क को केंद्रीकृत कर रहा था (इनमें से कुछ इन कैस्केडिंग परिवर्तनों के बीच में बार-बार ऐसा नहीं करने की उम्मीद में प्रकट होगा)।
व्यावहारिक प्रतिक्रिया: रचना
जिन चीज़ों के बारे में हम पहले (या कम से कम मैं) नोटिस कर रहे थे उनमें से एक यह था कि समस्याएँ पैदा IMotion
हो रही थीं, जो कि 100 अलग-अलग वर्गों द्वारा लागू की जा सकती हैं, लेकिन ठीक उसी कार्यान्वयन और राज्य से जुड़ी हैं। इसके अलावा, इसका उपयोग केवल कुछ मुट्ठी भर प्रणालियों जैसे प्रतिपादन, कीफ्रेम गति और भौतिकी द्वारा किया जाएगा।
तो इस तरह के मामले में, हम इंटरफ़ेस का उपयोग करने वाले सिस्टम के बीच 3 से 1 के रिश्ते की तरह हो सकते हैं, और इंटरफ़ेस को लागू करने वाले उपप्रकारों के बीच 100-से -1 संबंध।
3 क्लाइंट सिस्टम के बजाय जटिलता और रखरखाव तब 100 उपप्रकारों के कार्यान्वयन और रखरखाव के लिए काफी हद तक तिरछा हो जाएगा IMotion
। यह इन 100 उपप्रकारों के रखरखाव के लिए हमारे सभी रखरखाव कठिनाइयों को स्थानांतरित करता है, न कि इंटरफ़ेस का उपयोग करने वाले 3 स्थानों को। कोड में कुछ या नहीं "अप्रत्यक्ष अपक्षय युग्मन" के साथ 3 स्थानों को अपडेट करना (जैसा कि इसमें निर्भरता है लेकिन अप्रत्यक्ष रूप से एक इंटरफ़ेस के माध्यम से, प्रत्यक्ष निर्भरता नहीं), कोई बड़ी बात नहीं: "अप्रत्यक्ष प्रतिरूप कपलिंग" के बोटलोड के साथ 100 उपप्रकार स्थानों को अपडेट करना , बहुत बड़ी बात *।
* मुझे लगता है कि कार्यान्वयन परिप्रेक्ष्य से इस अर्थ में "अपवाही कपलिंग" की परिभाषा के साथ पंगा लेना अजीब और गलत है, मैं इंटरफ़ेस से संबंधित रखरखाव जटिलता का वर्णन करने के लिए एक बेहतर तरीका नहीं पाया गया है जब दोनों इंटरफ़ेस और सौ उपप्रकारों के अनुरूप कार्यान्वयन बदलना होगा।
इसलिए मुझे कड़ी मेहनत करनी पड़ी लेकिन मैंने प्रस्ताव रखा कि हम थोड़ा और व्यावहारिक बनने की कोशिश करें और पूरे "शुद्ध इंटरफ़ेस" विचार को आराम दें। यह मेरे लिए कोई मतलब नहीं था IMotion
जब तक कि हम इसे लागू करने का एक समृद्ध विविधता होने का एक फायदा नहीं देखा जब तक कि पूरी तरह से अमूर्त और स्टेटलेस जैसी कुछ बनाने के लिए। हमारे मामले में, IMotion
एक समृद्ध विविधता के कार्यान्वयन के लिए वास्तव में काफी रखरखाव दुःस्वप्न में बदल जाएगा, क्योंकि हम विविधता नहीं चाहते थे । इसके बजाय हम एक एकल गति कार्यान्वयन बनाने की कोशिश कर रहे थे, जो कि ग्राहकों की आवश्यकताओं को बदलने के खिलाफ वास्तव में अच्छा है, और अक्सर शुद्ध इंटरफ़ेस विचार के आसपास काम कर रहे थे, जो प्रत्येक कार्यान्वयनकर्ता IMotion
को उसी कार्यान्वयन और राज्य से जुड़े उपयोग करने के लिए मजबूर करने की कोशिश कर रहा था ताकि हम डॉन ' t डुप्लिकेट लक्ष्य।
इस प्रकार इंटरफेस Behaviors
एक इकाई के साथ व्यापक रूप से जुड़े हुए हो गए । IMotion
बस एक Motion
"घटक" बन जाएगा (मैंने COM से दूर "घटक" को जिस तरह से परिभाषित किया है, वह एक सामान्य परिभाषा के करीब है, जहां एक टुकड़ा "पूर्ण" इकाई बनता है)।
इसके अलावा:
class IMotion
{
public:
virtual ~IMotion() {}
virtual void transform(const Matrix& mat) = 0;
...
};
हमने इसे कुछ इस तरह विकसित किया:
class Motion
{
public:
void transform(const Matrix& mat)
{
...
}
...
private:
Matrix transformation;
...
};
यह अवलंबन उलटने के सिद्धांत का एक नितांत उल्लंघन है, जो अमूर्त से कंक्रीट में वापस जाना शुरू करता है, लेकिन मेरे लिए अमूर्तता का ऐसा स्तर केवल तभी उपयोगी है, जब हम किसी भविष्य में एक वास्तविक आवश्यकता की पूर्ति कर सकते हैं, एक उचित संदेह से परे और नहीं इस तरह के लचीलेपन के लिए "क्या होगा अगर" परिदृश्य पूरी तरह से उपयोगकर्ता के अनुभव (जो शायद किसी भी तरह से एक डिजाइन परिवर्तन की आवश्यकता होगी) से अलग हो गए हैं, तो व्यायाम करना।
इसलिए हमने इस डिजाइन को विकसित करना शुरू कर दिया। QueryInterface
जैसे और हो गया QueryBehavior
। इसके अलावा, यहाँ विरासत का उपयोग करना बेकार लगने लगा। हमने इसके बजाय रचना का उपयोग किया। ऑब्जेक्ट उन घटकों के संग्रह में बदल गए जिनकी उपलब्धता को चलाने और रनटाइम में इंजेक्ट किया जा सकता है।
कुछ पेशेवरों:
- पिछली, शुद्ध-इंटरफ़ेस COM-शैली प्रणाली की तुलना में हमारे मामले में अभी भी बनाए रखना बहुत आसान था। आवश्यकताओं या वर्कफ़्लो शिकायतों में परिवर्तन की तरह अप्रत्याशित आश्चर्य को बहुत आसानी से एक बहुत केंद्रीय और स्पष्ट
Motion
कार्यान्वयन के साथ समायोजित किया जा सकता है , उदाहरण के लिए, और एक सौ उपप्रकारों में छितराया नहीं गया।
- वास्तव में जिस तरह की जरूरत थी, उसके लचीलेपन का एक नया स्तर दिया। हमारी पिछली प्रणाली में, चूंकि वंशानुक्रम एक स्थिर संबंध मॉडल करता है, इसलिए हम केवल C ++ में संकलन-समय पर नई संस्थाओं को प्रभावी ढंग से परिभाषित कर सकते हैं। हम इसे स्क्रिप्टिंग लैंग्वेज से नहीं कर सकते हैं, जैसे कि कंपोजिशन एप्रोच के साथ, हम रनटाइम पर फ्लाई पर नए एंटिटीज को एक साथ जोड़कर उन्हें कंपोनेंट में जोड़ सकते हैं और उन्हें एक लिस्ट में जोड़ सकते हैं। एक "इकाई" एक रिक्त कैनवस में बदल गया, जिस पर हम बस मक्खी पर जो कुछ भी हम चाहते थे उसका एक कोलाज फेंक सकते थे, जिसके परिणामस्वरूप संबंधित सिस्टम स्वचालित रूप से इन संस्थाओं को पहचानते और संसाधित करते हैं।
कुछ विपक्ष:
- हमारे पास दक्षता विभाग में कठिन समय था, और प्रदर्शन-महत्वपूर्ण क्षेत्रों में स्थिरता थी। प्रत्येक प्रणाली अभी भी संस्थाओं के उन घटकों को कैश करना चाहती है जो इन व्यवहारों को प्रदान करते हैं ताकि उन सभी के माध्यम से बार-बार लूपिंग से बचा जा सके और जो उपलब्ध था उसकी जांच हो। प्रदर्शन की मांग करने वाली प्रत्येक प्रणाली ऐसा कभी-कभी थोड़ा अलग तरीके से करती है, और इस कैश्ड सूची को अद्यतन करने में विफल रहने और संभवतः डेटा संरचना (यदि खोज का कोई रूप कुंठित या अशक्त करना जैसे कुछ शामिल था) में बग के एक अलग सेट के लिए प्रवण था। अस्पष्ट दृश्य परिवर्तन घटना, उदाहरण के लिए
- अभी भी कुछ अजीब और जटिल था कि मैं इन सभी दानेदार छोटे व्यवहार, सरल वस्तुओं से संबंधित पर अपनी उंगली नहीं डाल सकता था। हमने अभी भी इन "व्यवहार" वस्तुओं के बीच बातचीत से निपटने के लिए बहुत सारी घटनाओं को जन्म दिया है जो कभी-कभी आवश्यक थे, और परिणाम बहुत ही विकेन्द्रीकृत कोड था। प्रत्येक छोटी वस्तु को शुद्धता के लिए परीक्षण करना आसान था और, व्यक्तिगत रूप से लिया गया, अक्सर पूरी तरह से सही थे। फिर भी ऐसा महसूस हुआ कि हम एक छोटे से गाँव से बने एक विशाल पारिस्थितिकी तंत्र को बनाए रखने की कोशिश कर रहे थे और इस बात की कोशिश कर रहे थे कि वे सभी व्यक्तिगत रूप से क्या करें और समग्र रूप से बनायें। सी-स्टाइल 80 के दशक के कोडबेस को एक महाकाव्य की तरह महसूस हुआ, मेगालोपोलिस को ओवरपॉप किया गया, जो निश्चित रूप से एक रखरखाव दुःस्वप्न था,
- अमूर्तता की कमी के साथ लचीलेपन का नुकसान लेकिन एक ऐसे क्षेत्र में जहां हमें वास्तव में कभी भी इसके लिए वास्तविक आवश्यकता का सामना नहीं करना पड़ा, इसलिए शायद ही एक व्यावहारिक चुनाव (हालांकि कम से कम एक सैद्धांतिक एक)।
- ABI संगतता को बनाए रखना हमेशा कठिन होता था, और इसने स्थिर डेटा की आवश्यकता को पूरा करके कठिन बना दिया और न कि "व्यवहार" से जुड़े स्थिर इंटरफ़ेस को। हालाँकि, हम आसानी से नए व्यवहार जोड़ सकते हैं और अगर मौजूदा परिवर्तन की आवश्यकता है, तो मौजूदा लोगों को अलग कर सकते हैं, और संस्करण संबंधी चिंताओं को संभालने के लिए उप-स्तर पर इंटरफेस के नीचे बैकफ्लिप करने की तुलना में यकीनन आसान था।
एक घटना जो घटित हुई, वह यह थी कि चूंकि हमने इन व्यवहार घटकों पर अमूर्तता खो दी थी, इसलिए हमारे पास उनमें से अधिक था। उदाहरण के लिए, एक सार IRenderable
घटक के बजाय , हम एक ठोस Mesh
या PointSprites
घटक के साथ एक वस्तु संलग्न करेंगे । रेंडरिंग सिस्टम को पता होगा कि रेंडर Mesh
और PointSprites
कंपोनेंट्स कैसे मिलते हैं और ऐसे कंपोनेंट्स को ढूंढते हैं जो ऐसे कंपोनेंट्स मुहैया कराते हैं। अन्य समय में, हमारे पास विविध रेंडर थे, जैसे SceneLabel
कि हमें पता चला कि हमें hindsight में आवश्यक था, और इसलिए हम SceneLabel
उन मामलों में संबंधित संस्थाओं (संभवतः इसके अलावा Mesh
) में संलग्न करेंगे । रेंडरिंग सिस्टम का कार्यान्वयन तब यह जानने के लिए अपडेट किया जाएगा कि उन संस्थाओं को कैसे प्रदान किया जाए जो उन्हें प्रदान करते हैं, और यह एक बहुत ही आसान बदलाव था।
इस मामले में, घटकों से बनी एक इकाई को तब किसी अन्य इकाई के घटक के रूप में भी इस्तेमाल किया जा सकता है। हम लेगो ब्लॉक को हुक करके इस तरह से चीजों का निर्माण करेंगे।
ईसीएस: सिस्टम और कच्चे डेटा घटक
वह अंतिम प्रणाली जहां तक मैंने इसे अपने दम पर बनाया था, और हम अभी भी इसे COM के साथ कम कर रहे थे। ऐसा लगा कि यह एक इकाई-घटक प्रणाली बनना चाहता है, लेकिन मैं उस समय इससे परिचित नहीं था। मैं कॉम-शैली के उदाहरणों के आसपास देख रहा था, जिन्होंने मेरे क्षेत्र को संतृप्त किया, जब मुझे वास्तु प्रेरणा के लिए एएए गेम इंजनों को देखना चाहिए था। मैंने आखिरकार ऐसा करना शुरू कर दिया।
मुझे जो याद आ रहा था वह कई प्रमुख विचार थे:
- "घटकों" को संसाधित करने के लिए "सिस्टम" की औपचारिकता।
- "अवयव" व्यवहार डेटा के बजाय कच्चे ऑब्जेक्ट होने के कारण एक बड़ी वस्तु में एक साथ रचे गए।
- घटकों के संग्रह से जुड़ी सख्त आईडी से अधिक कुछ भी नहीं है।
मैंने आखिरकार उस कंपनी को छोड़ दिया और एक ईसीएस पर एक इंडी के रूप में काम करना शुरू कर दिया (अभी भी अपनी बचत को खत्म करते हुए इस पर काम कर रहा है), और यह अब तक का सबसे आसान सिस्टम है।
ईसीएस दृष्टिकोण के साथ मैंने जो देखा वह यह था कि यह उन समस्याओं को हल करता है जो मैं अभी भी ऊपर से संघर्ष कर रहा था। मेरे लिए सबसे महत्वपूर्ण बात यह है कि हम ऐसा महसूस कर रहे थे कि हम जटिल बातचीत वाले छोटे छोटे गाँवों के बजाय स्वस्थ-आकार वाले "शहरों" का प्रबंधन कर रहे थे। अपनी जनसंख्या को प्रभावी ढंग से प्रबंधित करने के लिए एक बड़ी "मेगालोपोलिस" के रूप में इसे बनाए रखना उतना कठिन नहीं था, लेकिन छोटे-छोटे गांवों से भरी दुनिया के रूप में अराजक नहीं थे, जहां एक-दूसरे के साथ बातचीत करते हुए बस व्यापार मार्गों के बारे में सोच रहे थे। उनके बीच एक बुरे सपने का ग्राफ बना। ईसीएस ने भारी "सिस्टम" के लिए सभी जटिलता को पूरा किया, जैसे एक रेंडरिंग सिस्टम, एक स्वस्थ आकार का "शहर", लेकिन "ओवरपॉलेटेड मेगालोपोलिस" नहीं।
कच्चे डेटा बनने वाले घटक मुझे पहली बार में अजीब लगे , क्योंकि यह OOP के सिद्धांत को छिपाने वाली बुनियादी जानकारी को भी तोड़ देता है। यह ओओपी के बारे में मेरे द्वारा प्रिय सबसे बड़े मूल्यों में से एक को चुनौती देने का एक प्रकार था, जो कि आक्रमणकारियों को बनाए रखने की क्षमता थी जिसके लिए एनकैप्सुलेशन और सूचना छिपाना आवश्यक था। लेकिन यह एक गैर-चिंता का विषय बन गया क्योंकि यह जल्दी से स्पष्ट हो गया कि सिर्फ एक दर्जन या इतने व्यापक सिस्टम के साथ क्या हो रहा था कि इस तरह के तर्क को बदलने के बजाय डेटा को सैकड़ों से हजारों उपप्रकारों में फैलाया जा रहा था, जो इंटरफेस का कॉम्बो लागू कर रहे थे। मैं इसे ओओपी-शैली के फैशन में अभी भी इस तरह से सोचने की प्रवृत्ति रखता हूं, सिवाय इसके कि फैलता है जहां सिस्टम कार्यक्षमता और कार्यान्वयन प्रदान कर रहे हैं जो डेटा तक पहुंचते हैं, घटक डेटा प्रदान कर रहे हैं, और इकाइयां घटक प्रदान कर रही हैं।
यह और भी आसान हो गया , प्रति-सहज रूप से, सिस्टम के कारण होने वाले दुष्प्रभावों के बारे में, जब डेटा को व्यापक रूप से परिवर्तित करने वाले कुछ मुट्ठी भर सिस्टम थे। प्रणाली बहुत "चापलूसी" बन गई, मेरे कॉल स्टैक प्रत्येक थ्रेड के लिए पहले से कहीं अधिक उथले हो गए। मैं उस ओवरसियर स्तर पर प्रणाली के बारे में सोच सकता था और अजीब आश्चर्य में नहीं दौड़ सकता था।
इसी तरह, यह उन प्रश्नों को समाप्त करने के संबंध में प्रदर्शन-महत्वपूर्ण क्षेत्रों को भी सरल बनाता है। चूँकि "सिस्टम" का विचार बहुत औपचारिक हो गया, एक प्रणाली उन घटकों की सदस्यता ले सकती थी जिनकी इसमें रुचि थी और बस उन संस्थाओं की एक कैश्ड सूची सौंपी जाए जो उस मानदंड को पूरा करती हैं। प्रत्येक व्यक्ति को उस कैशिंग अनुकूलन का प्रबंधन करने की आवश्यकता नहीं थी, यह एक ही स्थान पर केंद्रीकृत हो गया।
कुछ पेशेवरों:
- लगता है कि लगभग हर बड़ी आर्किटेक्चरल समस्या को हल करने के लिए मैं अपने कैरियर में बिना किसी डिजाइन के कोने में फंस गया था जब अप्रत्याशित जरूरतों का सामना कर रहा था।
कुछ विपक्ष:
- मेरे पास अभी भी एक कठिन समय है जो मेरे सिर को कभी-कभी उसके चारों ओर लपेटता है, और यह खेल उद्योग के भीतर भी सबसे परिपक्व या अच्छी तरह से स्थापित प्रतिमान नहीं है, जहां लोग इसके बारे में ठीक-ठीक तर्क देते हैं कि इसका क्या अर्थ है और कैसे करना है। यह निश्चित रूप से ऐसा कुछ नहीं है जो मैं पूर्व टीम के साथ कर सकता था, जिसमें मैंने काम किया था, जिसमें COM-शैली की मानसिकता या मूल कोडबेस की 1980 के दशक की C- शैली की मानसिकता शामिल थी। जहां मैं कभी-कभी भ्रमित हो जाता हूं, जैसे कि घटकों के बीच ग्राफ-शैली के संबंधों को कैसे मॉडल करना है, लेकिन मैंने हमेशा एक समाधान पाया है जो बाद में भयानक नहीं हुआ, जहां मैं बस एक घटक को दूसरे पर निर्भर बना सकता हूं ("इस गति" घटक माता-पिता के रूप में इस दूसरे पर निर्भर करता है, और सिस्टम एक ही पुनरावर्ती गति गणनाओं को करने से बचने के लिए संस्मरण का उपयोग करेगा ", उदाहरण के लिए)
- एबीआई अभी भी मुश्किल है, लेकिन अभी तक मैं यह कहने के लिए उद्यम करूंगा कि यह शुद्ध इंटरफ़ेस दृष्टिकोण से आसान है। यह मानसिकता में बदलाव है: डेटा स्थिरता, एबीएसआई के लिए एकमात्र फोकस बन जाता है, बजाय इंटरफ़ेस स्थिरता के, और कुछ मायनों में इंटरफ़ेस स्थिरता की तुलना में डेटा स्थिरता प्राप्त करना आसान है (उदा: फ़ंक्शन को बदलने के लिए कोई प्रलोभन सिर्फ इसलिए नहीं कि इसे एक नए पैरामीटर की आवश्यकता है। इस तरह का सामान मोटे सिस्टम इंप्लीमेंटेशन के अंदर होता है जो एबीआई को नहीं तोड़ता है)।
हालाँकि, क्या यह उचित है कि गेम इंजनों में कॉमनर-एंटिटी-सिस्टम आर्किटेक्चर का उपयोग करके एप्लिकेशन बनाएं?
तो वैसे भी, मैं बिल्कुल "हाँ" कहूंगा, मेरे व्यक्तिगत VFX उदाहरण के साथ एक मजबूत उम्मीदवार। लेकिन यह अभी भी गेमिंग की जरूरतों के समान है।
मैंने इसे खेल के इंजनों की चिंताओं से पूरी तरह से अलग किए गए दूरदराज के क्षेत्रों में अभ्यास करने के लिए नहीं रखा है (वीएफएक्स काफी समान है), लेकिन मुझे ऐसा लगता है कि कहीं अधिक क्षेत्र एक ईसीएस दृष्टिकोण के लिए अच्छे उम्मीदवार हैं। शायद यहां तक कि एक जीयूआई प्रणाली एक के लिए उपयुक्त होगी, लेकिन मैं अभी भी वहां एक अधिक ओओपी दृष्टिकोण का उपयोग करता हूं (लेकिन क्यूटी के विपरीत गहरी विरासत के बिना, जैसे)।
यह व्यापक रूप से बेरोज़गार क्षेत्र है, लेकिन यह मेरे लिए उपयुक्त लगता है जब भी आपकी संस्थाएं "लक्षणों" के एक समृद्ध संयोजन से बनी हो सकती हैं (और वास्तव में क्या लक्षण प्रदान करती हैं जो वे कभी भी परिवर्तन के अधीन हो सकते हैं), और जहां आपके पास सामान्यीकृत का एक मुट्ठी भर है ऐसी प्रणालियाँ जो उन संस्थाओं को संसाधित करती हैं जिनमें आवश्यक लक्षण होते हैं।
यह उन मामलों में किसी भी परिदृश्य के लिए एक बहुत ही व्यावहारिक विकल्प बन जाता है, जहां आपको कई तरह की विरासत या अवधारणा का मिश्रण (मिक्सिन्स, जैसे) का उपयोग करने के लिए लुभाया जा सकता है, केवल एक गहरी विरासत वंशानुक्रम या सैकड़ों कॉम्बोस में सैकड़ों या अधिक कॉम्बो का उत्पादन करने के लिए। एक फ्लैट पदानुक्रम में कक्षाओं की एक विशिष्ट कॉम्बो इंटरफेस को लागू करने, लेकिन जहां आपके सिस्टम संख्या (दर्जनों, जैसे) में कम हैं।
उन मामलों में, कोडबेस की जटिलता टाइप संयोजनों की संख्या के बजाय सिस्टम की संख्या के लिए अधिक आनुपातिक महसूस करने लगती है, क्योंकि प्रत्येक प्रकार अब केवल कंपोजिंग कंपोनेंट है जो कच्चे डेटा से ज्यादा कुछ नहीं है। जीयूआई सिस्टम स्वाभाविक रूप से इस प्रकार के चश्मे फिट करते हैं जहां उनके पास अन्य आधार प्रकार या इंटरफेस से संयुक्त सैकड़ों संभावित विजेट प्रकार हो सकते हैं, लेकिन उन्हें (लेआउट सिस्टम, रेंडरिंग सिस्टम आदि) संसाधित करने के लिए केवल कुछ मुट्ठी भर सिस्टम हैं। यदि GUI प्रणाली में ECS का उपयोग किया जाता है, तो सिस्टम की शुद्धता के बारे में तर्क करना शायद इतना आसान होगा, जब विरासत वाले इंटरफेस या बेस कक्षाओं के साथ सैकड़ों अलग-अलग ऑब्जेक्ट प्रकारों के बजाय इन सिस्टम के एक मुट्ठी भर द्वारा सभी कार्यक्षमता प्रदान की जाती है। यदि GUI प्रणाली में ECS का उपयोग किया जाता है, तो विगेट्स की कोई कार्यक्षमता नहीं होगी, केवल डेटा। विजेट संस्थाओं को प्रोसेस करने वाले केवल मुट्ठी भर सिस्टम में कार्यक्षमता होगी। एक विजेट के लिए कैसे अधिक महत्वपूर्ण घटनाओं को संभाला जाएगा यह मेरे से परे है, लेकिन अभी तक मेरे सीमित अनुभव के आधार पर, मुझे ऐसा मामला नहीं मिला है जहां उस प्रकार के तर्क को किसी दिए गए सिस्टम में एक तरह से केंद्र में स्थानांतरित नहीं किया जा सकता है, जिसमें दृष्टिहीनता, एक बहुत अधिक सुरुचिपूर्ण समाधान निकला, जिसकी मुझे कभी उम्मीद थी।
मैं इसे अधिक क्षेत्रों में नियोजित देखना पसंद करूंगा, क्योंकि यह मेरा जीवन रक्षक था। बेशक यह बीमार-अनुकूल है अगर आपका डिज़ाइन इस तरह से टूटता नहीं है, तो संस्थाओं को घटकों को एकत्रित करने से लेकर उन प्रणालियों को बनाने वाली प्रणालियों तक को समेटना, लेकिन अगर वे स्वाभाविक रूप से इस तरह के मॉडल को फिट करते हैं, तो यह सबसे अद्भुत चीज है जिसका मैंने अभी तक सामना किया है। ।