मैं माइक एक्टन के 3 बड़े झूठों को पढ़कर शुरू करने का सुझाव दूंगा, क्योंकि आप उनमें से दो का उल्लंघन करते हैं। मैं गंभीर हूं, यह आपके कोड को डिजाइन करने के तरीके को बदल देगा: http://cellperformance.beyond3d.com/articles/2008/03/three-big-lies.html
तो आप किसका उल्लंघन करते हैं?
झूठ # 3 - कोड डेटा से अधिक महत्वपूर्ण है
आप निर्भरता इंजेक्शन के बारे में बात करते हैं, जो कुछ (और केवल कुछ) उदाहरणों में उपयोगी हो सकता है लेकिन हमेशा एक बड़ा बड़ा अलार्म घंटी बजना चाहिए यदि आप इसका उपयोग करते हैं, खासकर खेल विकास में! क्यों? क्योंकि यह अक्सर अनावश्यक अमूर्तता है। और गलत स्थानों पर अमूर्त भयानक हैं। तो आपके पास एक खेल है। गेम में विभिन्न घटकों के लिए प्रबंधक हैं। घटक सभी परिभाषित हैं। तो अपने मुख्य गेम लूप कोड में कहीं एक वर्ग बनाएं जो "प्रबंधकों" के पास है। पसंद:
private CollissionManager _collissionManager;
private BulletManager _bulletManager;
प्रत्येक प्रबंधक वर्ग (getBulletManager ()) प्राप्त करने के लिए इसे कुछ गेट्टर फ़ंक्शन दें। हो सकता है कि यह वर्ग स्वयं एक सिंगलटन हो या यह एक से उपलब्ध हो (आपको शायद केंद्रीय गेम सिंगलटन कहीं भी मिला हो)। अच्छी तरह से परिभाषित हार्ड-कोडित डेटा और व्यवहार के साथ कुछ भी गलत नहीं है।
एक प्रबंधक प्रबंधित न करें जो आपको प्रबंधकों को एक कुंजी का उपयोग करके पंजीकृत करने देता है, जिसे उस कुंजी का उपयोग करके अन्य वर्गों द्वारा पुनर्प्राप्त किया जा सकता है जो प्रबंधक का उपयोग करना चाहते हैं। यह एक महान प्रणाली है और बहुत ही लचीली है, लेकिन यहां एक खेल के बारे में बात करना कहां है। आपको पता है कि कौन से सिस्टम गेम में हैं। आप जैसा नाटक क्यों नहीं करते? क्योंकि यह उन लोगों के लिए एक प्रणाली है जो सोचते हैं कि कोड डेटा से अधिक महत्वपूर्ण है। वे कहेंगे "कोड लचीला है, डेटा इसे भरता है"। लेकिन कोड सिर्फ डेटा है। मेरे द्वारा वर्णित प्रणाली कहीं अधिक आसान, अधिक विश्वसनीय, बनाए रखने में आसान और बहुत अधिक लचीली है (उदाहरण के लिए, यदि एक प्रबंधक का व्यवहार अन्य प्रबंधकों से भिन्न होता है, तो आपको पूरी प्रणाली को पुनः व्यवस्थित करने के बजाय केवल कुछ पंक्तियों को बदलना होगा)
झूठ # 2 - कोड दुनिया के एक मॉडल के आसपास डिज़ाइन किया जाना चाहिए
तो आपके पास खेल की दुनिया में एक इकाई है। इकाई के पास अपने व्यवहार को परिभाषित करने वाले कई घटक होते हैं। तो आप घटक वस्तुओं की सूची के साथ एक इकाई वर्ग बनाते हैं, और एक अद्यतन () फ़ंक्शन जो प्रत्येक घटक के अद्यतन () फ़ंक्शन को कॉल करता है। सही?
नहींं :) यह दुनिया के एक मॉडल के आसपास डिजाइन कर रहा है: आपके पास अपने खेल में बुलेट है, इसलिए आप एक बुलेट क्लास जोड़ते हैं। फिर आप प्रत्येक बुलेट को अपडेट करते हैं और अगले एक पर जाते हैं। यह आपके प्रदर्शन को बिल्कुल मार देगा और यह आपको हर जगह डुप्लिकेट कोड के साथ एक भयानक जटिल कोडबेस देता है और समान कोड की कोई तार्किक संरचना नहीं है। ( पारंपरिक ओओ डिजाइन क्यों बेकार है, या डेटा ओरिएंटेड डिजाइन देखें) के बारे में अधिक विस्तार से स्पष्टीकरण के लिए यहां मेरे जवाब की जांच करें )
आइए हमारे ओओ पूर्वाग्रह के बिना स्थिति पर एक नज़र डालें। हम निम्नलिखित चाहते हैं, कोई कम नहीं (कृपया ध्यान दें कि इकाई या वस्तु के लिए एक वर्ग बनाने की कोई आवश्यकता नहीं है):
- आपके पास संस्थाओं का एक समूह है
- संस्थाओं में कई घटक शामिल होते हैं जो इकाई के व्यवहार को परिभाषित करते हैं
- आप खेल में प्रत्येक फ्रेम को अधिमानतः नियंत्रित तरीके से प्रत्येक घटक को अपडेट करना चाहते हैं
- एक साथ संबंधित घटकों की पहचान करने के अलावा, कुछ भी नहीं है जो इकाई को स्वयं करने की आवश्यकता है। यह कुछ घटकों के लिए एक लिंक / आईडी है।
और स्थिति को देखते हैं। आपका कंपोनेंट सिस्टम गेम के हर फ्रेम में हर ऑब्जेक्ट के व्यवहार को अपडेट करेगा । यह निश्चित रूप से आपके इंजन की एक महत्वपूर्ण प्रणाली है। यहाँ प्रदर्शन महत्वपूर्ण है!
यदि आप कंप्यूटर आर्किटेक्चर या डेटा ओरिएंटेड डिज़ाइन से परिचित हैं, तो आप जानते हैं कि सर्वश्रेष्ठ प्रदर्शन कैसे प्राप्त किया जाता है: कसकर पैक की गई मेमोरी और कोड निष्पादन द्वारा। यदि आप कोड A, B और C के स्निपेट को इस तरह निष्पादित करते हैं: ABCABCABC, तो आपको उसी तरह का प्रदर्शन नहीं मिलेगा जब आप इसे इस तरह निष्पादित करते हैं: AAABBBCCC। यह सिर्फ इसलिए नहीं है कि निर्देश और डेटा कैश का अधिक कुशलता से उपयोग किया जाएगा, बल्कि इसलिए भी कि यदि आप एक के बाद एक सभी "ए" निष्पादित करते हैं, तो अनुकूलन के लिए बहुत जगह है: डुप्लिकेट कोड को हटाने, डेटा द्वारा उपयोग किए जाने वाले पूर्वगामी डेटा सभी "ए", आदि।
इसलिए यदि हम सभी घटकों को अद्यतन करना चाहते हैं, तो चलिए उन्हें अपडेट फ़ंक्शन के साथ कक्षाएं / ऑब्जेक्ट नहीं बनाते हैं। आइए प्रत्येक इकाई के प्रत्येक घटक के लिए उस अपडेट फ़ंक्शन को कॉल न करें। यह "ABCABCABC" समाधान है। आइए सभी समान घटक अपडेट को एक साथ समूहीकृत करें। फिर हम सभी ए-घटकों को अपडेट कर सकते हैं, इसके बाद बी आदि। हमें इसे बनाने की क्या आवश्यकता है?
सबसे पहले, हमें घटक प्रबंधकों की आवश्यकता है। खेल में हर प्रकार के घटक के लिए, हमें प्रबंधक वर्ग की आवश्यकता है। इसमें एक अपडेट फ़ंक्शन है जो उस प्रकार के सभी घटकों को अपडेट करेगा। इसका एक फ़ंक्शन है जो उस प्रकार का एक नया घटक जोड़ देगा और एक फ़ंक्शन हटाएगा जो निर्दिष्ट घटक को नष्ट कर देगा। उस घटक के लिए विशिष्ट डेटा प्राप्त करने और सेट करने के लिए अन्य सहायक कार्य हो सकते हैं (जैसे: मॉडल घटक के लिए 3 डी मॉडल सेट करें)। ध्यान दें कि प्रबंधक कुछ मायनों में बाहरी दुनिया के लिए एक ब्लैक बॉक्स है। हम नहीं जानते कि प्रत्येक घटक का डेटा कैसे संग्रहीत किया जाता है। हम नहीं जानते कि प्रत्येक घटक कैसे अद्यतन किया जाता है। हम परवाह नहीं करते हैं, जब तक घटकों को व्यवहार करना चाहिए जैसा कि उन्हें करना चाहिए।
आगे हमें एक इकाई की आवश्यकता है। आप इसे एक वर्ग बना सकते हैं, लेकिन यह शायद ही आवश्यक है। एक इकाई एक अद्वितीय पूर्णांक आईडी या एक हैशेड स्ट्रिंग (एक पूर्णांक भी) से अधिक कुछ नहीं हो सकती है। जब आप इकाई के लिए एक घटक बनाते हैं, तो आप प्रबंधक के तर्क के रूप में आईडी पास करते हैं। जब आप घटक को निकालना चाहते हैं, तो आप फिर से आईडी पास करते हैं। केवल एक आईडी बनाने के बजाय इकाई में थोड़ा अधिक डेटा जोड़ने के कुछ फायदे हो सकते हैं, लेकिन वे केवल सहायक कार्य होंगे क्योंकि जैसा कि मैंने आवश्यकताओं में सूचीबद्ध किया है, सभी इकाई व्यवहार स्वयं घटकों द्वारा परिभाषित किया गया है। हालांकि यह आपका इंजन है, इसलिए वही करें जो आपके लिए मायने रखता है।
हमें क्या जरूरत है एक इकाई प्रबंधक है। यदि आप केवल ID समाधान का उपयोग करते हैं, या इसका उपयोग एंटिटी ऑब्जेक्ट बनाने / प्रबंधित करने के लिए किया जा सकता है, तो यह वर्ग या तो अद्वितीय ID जनरेट करेगा। यदि आपको इसकी आवश्यकता है तो यह गेम की सभी संस्थाओं की सूची भी रख सकता है। Entity Manager आपके घटक सिस्टम का केंद्रीय वर्ग हो सकता है, जो आपके गेम के सभी ComponentManagers के संदर्भों को संग्रहीत करता है और उनके अद्यतन कार्यों को सही क्रम में बुलाता है। इस तरह से सभी गेम लूप को कॉल करना है EntityManager.update () और पूरी प्रणाली आपके बाकी इंजन से अच्छी तरह से अलग हो गई है।
यह बर्ड-आई व्यू है, आइए एक नजर डालते हैं कि कंपोनेंट मैनेजर कैसे काम करते हैं। यहाँ आपको क्या चाहिए:
- जब (एंटिटी) को बनाया जाता है तो घटक डेटा बनाएं
- हटाए जाने पर (निकाय) को घटक डेटा हटाएं
- अपडेट होने पर सभी (लागू) घटक डेटा को अपडेट करें () कहा जाता है (यानी सभी घटकों को प्रत्येक फ्रेम को अपडेट करने की आवश्यकता नहीं है)
अंतिम वह जगह है जहां आप घटकों के व्यवहार / तर्क को परिभाषित करते हैं और आपके द्वारा लिखे जा रहे घटक के प्रकार पर पूरी तरह से निर्भर करता है। ऐनिमेशनकम्पोनेंट उस पर आधारित फ्रेम के एनिमेशन डेटा को अपडेट करेगा। DragableComponent केवल एक घटक को अपडेट करेगा जो माउस द्वारा खींचा जा रहा है। PhysicsComponent भौतिकी प्रणाली में डेटा को अपडेट करेगा। फिर भी, क्योंकि आप एक ही बार में एक ही प्रकार के सभी घटकों को अपडेट करते हैं, तो आप कुछ अनुकूलन कर सकते हैं जो तब संभव नहीं है जब प्रत्येक घटक एक अद्यतन फ़ंक्शन के साथ एक अलग ऑब्जेक्ट है जिसे किसी भी समय कॉल किया जा सकता है।
ध्यान दें कि मैंने अभी भी घटक डेटा रखने के लिए एक XxxComponent वर्ग के निर्माण के लिए कभी नहीं बुलाया है। यह आप पर निर्भर है। क्या आपको डेटा ओरिएंटेड डिज़ाइन पसंद है? फिर प्रत्येक चर के लिए अलग-अलग सरणियों में डेटा की संरचना करें। क्या आपको ऑब्जेक्ट ओरिएंटेड डिज़ाइन पसंद है? (मैं इसकी सिफारिश नहीं करूंगा, यह अभी भी आपके प्रदर्शन को कई स्थानों पर मार देगा) फिर एक XxxComponent ऑब्जेक्ट बनाएं जो प्रत्येक घटक के डेटा को रखेगा।
प्रबंधकों के बारे में बहुत अच्छी बात है। अब एनकैप्सुलेशन प्रोग्रामिंग की दुनिया में सबसे बुरी तरह से दुरुपयोग दर्शन में से एक है। इस तरह इसका उपयोग किया जाना चाहिए। केवल प्रबंधक को पता है कि किस घटक डेटा को कहाँ संग्रहीत किया जाता है, एक घटक का तर्क कैसे काम करता है। डेटा प्राप्त / सेट करने के लिए कुछ कार्य हैं, लेकिन यह है। आप पूरे प्रबंधक और उसके अंतर्निहित वर्गों को फिर से लिख सकते हैं और यदि आप सार्वजनिक इंटरफ़ेस नहीं बदलते हैं, तो कोई भी नोटिस नहीं करता है। भौतिकी इंजन बदल दिया? बस PhysicsComponentManager को फिर से लिखें और आपका काम हो गया।
फिर एक अंतिम बात है: घटकों के बीच संचार और डेटा साझाकरण। अब यह मुश्किल है और कोई एक आकार-फिट-सभी समाधान नहीं है। आप स्थिति घटक (यानी PositionManager.getPosition (निकाय)) से स्थिति प्राप्त करने के लिए टकराव घटक के लिए अनुमति देने के लिए प्रबंधकों में प्राप्त / सेट कार्य बना सकते हैं। आप एक ईवेंट सिस्टम का उपयोग कर सकते हैं। आप इकाई में कुछ साझा डेटा (मेरी राय में सबसे खराब समाधान) स्टोर कर सकते हैं। आप एक संदेश प्रणाली का उपयोग कर सकते हैं (यह अक्सर उपयोग किया जाता है)। या कई प्रणालियों के संयोजन का उपयोग करें! मेरे पास इनमें से प्रत्येक सिस्टम में जाने का समय या अनुभव नहीं है, लेकिन Google और स्टैकओवरफ़्लो खोज आपके मित्र हैं।