खेल वास्तुकला / डिजाइन पैटर्न पर सलाह


16

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

थोड़ी सी पृष्ठभूमि के लिए, मैंने पिछली गर्मियों में अपने खाली समय में इस पर काम करना शुरू किया। मैं शुरू में C # में गेम बना रहा था, लेकिन लगभग 3 महीने पहले, C ++ में स्विच करने का फैसला किया। मैं सी ++ पर एक अच्छा हैंडल प्राप्त करना चाहता था क्योंकि यह थोड़ी देर से है क्योंकि मैंने इसका भारी उपयोग किया है, और लगा कि यह एक अच्छा प्रेरक होगा। मैं बड़े पैमाने पर बूस्ट लाइब्रेरी का उपयोग कर रहा हूं और ऑडियो के लिए ग्राफिक्स और एफएमओडी के लिए एसएफएमएल का उपयोग कर रहा हूं।

मेरे पास कोड लिखा हुआ है, लेकिन मैं इसे खत्म करने और शुरू करने पर विचार कर रहा हूं।

यहां चिंता के प्रमुख क्षेत्र हैं जो मैं चाहता हूं कि कुछ लोगों ने उचित तरीके से कुछ राय प्राप्त की है और उन्हें हल किया है।

1. चक्रीय निर्भरता जब मैं C # में खेल कर रहा था, तो मुझे वास्तव में इस बारे में चिंता करने की आवश्यकता नहीं थी क्योंकि यह कोई मुद्दा नहीं है। C ++ में जाना, यह एक बहुत बड़ी समस्या बन गई है और मुझे लगता है कि मैंने चीजों को गलत तरीके से डिजाइन किया होगा। मैं वास्तव में कल्पना नहीं कर सकता कि मैं अपनी कक्षाओं को कैसे समाप्त कर सकता हूं और अभी भी उन्हें वही करना है जो मैं चाहता हूं। यहां निर्भरता श्रृंखला के कुछ उदाहरण दिए गए हैं:

मेरे पास एक स्थिति प्रभाव वर्ग है। किसी वर्ण के विरुद्ध प्रभाव लागू करने के लिए वर्ग में कई विधियाँ हैं (लागू करें / लागू करें, टिक करें, आदि)। उदाहरण के लिए,

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

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

2 - घटनाएँ

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

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

और मेरे चरित्र वर्ग में

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

जब भी चरित्र की प्रतिमा बदली, मैं इटरेट करूंगा और हर स्टेटचेंजफ़ंक्शन को मैप पर कॉल करूंगा। जबकि यह काम करता है, मुझे चिंता है कि यह काम करने के लिए एक बुरा दृष्टिकोण है।

3 - ग्राफिक्स

यह बड़ी बात है। यह मेरे द्वारा उपयोग किए जा रहे ग्राफिक्स लाइब्रेरी से संबंधित नहीं है, लेकिन एक वैचारिक चीज से अधिक है। C # में, मैंने अपनी कक्षाओं में बहुत से ग्राफिक्स जोड़े, जो मुझे पता है कि एक भयानक विचार है। यह करना चाहते थे इस बार मैंने अलग दृष्टिकोण की कोशिश की।

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

मैंने एक स्क्रीन मैनेजर डिज़ाइन किया है जो उपयोगकर्ता इनपुट के आधार पर पुश और पॉप स्क्रीन करेगा।

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

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

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

जवाबों:


15

कुल मिलाकर मैं यह नहीं कहूंगा कि आपने जो कुछ भी सूचीबद्ध किया है, उससे आपको सिस्टम को स्क्रैप करना और शुरू करना चाहिए। यह कुछ ऐसा है जो प्रत्येक प्रोग्रामर किसी भी परियोजना के माध्यम से लगभग 50-75% करना चाहता है, जिस पर वे काम कर रहे हैं, लेकिन यह विकास के कभी न खत्म होने वाले चक्र की ओर जाता है और कभी भी कुछ भी खत्म नहीं करता है। तो, उस अंत तक, कुछ प्रत्येक अनुभाग पर वापस फ़ीड करते हैं।

  1. यह एक समस्या हो सकती है लेकिन आमतौर पर किसी भी चीज़ की तुलना में अधिक झुंझलाहट होती है। क्या आप #pragma का उपयोग एक बार कर रहे हैं या #ifndef MY_HEADER_FILE_H #define MY_HEADER_FILE_H ... क्रमशः आपकी शीर्ष पर या आपके .h फ़ाइलों के आसपास #endif? इस तरह से .h फ़ाइल केवल एक बार प्रत्येक दायरे में मौजूद है? यदि आप हैं, तो मेरी सिफारिश फिर से सभी संकलित कथनों और संकलन को हटाकर, फिर से खेल को संकलित करने की आवश्यकता के रूप में जोड़ देती है।

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

  3. यह मुझे सही रास्ते पर भी लगता है और व्यक्तिगत और व्यावसायिक दोनों तरह से मैं अपने स्वयं के इंजन के लिए करता हूं। यह मेन्यू सिस्टम को एक स्टेट सिस्टम बनाता है जिसमें या तो रूट मेनू होता है (गेम शुरू होने से पहले) या प्लेयर HUD को 'रूट' स्क्रीन के रूप में प्रदर्शित किया जाता है, जो इस बात पर निर्भर करता है कि आपने इसे कैसे सेट किया है।

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

उम्मीद है की यह मदद करेगा!


#ifdef परिपत्र को समस्याओं को शामिल करने में मदद नहीं करता है।
कम्युनिस्ट डक

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

सलाह के लिए धन्यवाद :) खुशी है कि मुझे पता है कि मैं सही रास्ते पर हूँ
user127817

4

आपकी चक्रीय निर्भरता तब तक समस्या नहीं होनी चाहिए जब तक कि आप उन कक्षाओं को घोषित कर रहे हैं जहाँ आप शीर्ष लेख फ़ाइलों में और वास्तव में उन्हें .cpp (या जो भी) फ़ाइलों में शामिल कर सकते हैं।

घटना प्रणाली के लिए, दो सुझाव:

1) यदि आप अभी जिस पैटर्न का उपयोग कर रहे हैं उसे रखना चाहते हैं, तो std :: map के बजाय एक बढ़ावा :: unordered_map पर स्विच करने पर विचार करें। कुंजी के रूप में स्ट्रिंग्स के साथ मैपिंग धीमी है, विशेष रूप से। क्योंकि गति बढ़ाने में मदद करने के लिए .NET कुछ अच्छा काम करता है। Unordered_map का उपयोग करके स्ट्रिंग्स को राख कर दिया जाता है ताकि तुलना आम तौर पर तेज हो।

2) बढ़ावा देने जैसे कुछ अधिक शक्तिशाली स्विच करने पर विचार करें :: संकेत। यदि आप ऐसा करते हैं, तो आप अच्छी चीजें कर सकते हैं जैसे कि अपने गेम ऑब्जेक्ट्स को बढ़ावा देने से प्राप्त करने योग्य ट्रैक करें :: सिग्नल :: ट्रैक करने योग्य, और विध्वंसक को इवेंट सिस्टम से मैन्युअल रूप से अपंजीकृत होने के बजाय सब कुछ साफ करने का ध्यान रखें। तुम भी प्रत्येक स्लॉट की ओर इशारा करते (या इसके विपरीत, मैं सटीक नामकरण याद नहीं है) इसलिए यह बहुत कर के समान है अनेक संकेतों हो सकता है +=एक पर delegateसी # में। बढ़ावा देने के साथ सबसे बड़ी समस्या :: संकेत यह है कि इसे संकलित किया जाना है, यह सिर्फ हेडर नहीं है, इसलिए आपके प्लेटफॉर्म पर निर्भर करता है कि यह उठने और चलने में दर्द हो सकता है।

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