C ++ में गेम इंजन कैसे व्यवस्थित करें? क्या मेरी विरासत का उपयोग एक अच्छा विचार है?


11

मैं खेल के विकास और प्रोग्रामिंग दोनों में एक शुरुआती हूं।

मैं खेल इंजन के निर्माण में कुछ सिद्धांत सीखने की कोशिश कर रहा हूं।
मैं एक साधारण खेल बनाना चाहता हूं, मैं उस बिंदु पर हूं जहां मैं खेल इंजन को लागू करने की कोशिश कर रहा हूं।

इसलिए मैंने सोचा कि मेरे गेम इंजन को इस चीजों को नियंत्रित करना चाहिए:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

मैंने अपनी वस्तुओं को इस तरह डिजाइन किया:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

और मेरे खेल में 4 अलग-अलग वस्तुएं हैं: विमान, बाधा, खिलाड़ी, बुलेट और मैंने उन्हें इस तरह डिजाइन किया है:

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

अब गेम इंजन में मैं एक सामान्य ऑब्जेक्ट सूची रखना चाहता हूं, जहां मैं टक्कर के लिए उदाहरण के लिए जांच कर सकता हूं, ऑब्जेक्ट ले जा सकता हूं, और इसी तरह, लेकिन मैं यह भी चाहता हूं कि गेम इंजन खिलाड़ी को नियंत्रित करने के लिए उपयोगकर्ता कमांड लेगा ...

यह एक अच्छा विचार है?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

GameEngine कंस्ट्रक्टर इस तरह होगा:

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

तथ्य यह है कि मैं एक पॉइंटर का उपयोग कर रहा हूं क्योंकि मुझे कॉल करने की आवश्यकता fire()है जो प्लेयर ऑब्जेक्ट के लिए अद्वितीय है।

तो मेरा सवाल है: यह एक अच्छा विचार है? क्या यहाँ वंशानुक्रम का उपयोग गलत है?



मैंने उन्हें पोस्ट करने के बाद देखा है, और वास्तव में रचना एक महान विचार है! मैं हमेशा एक असली सी ++ डिजाइन होने के बजाय कक्षाओं के साथ सी का उपयोग करने का डर है!
पेला 86

मुझे लगता है कि विरासत की तुलना में रचना का उपयोग करने के कई और कारण हैं ... मेरा उत्तर लचीलेपन के फायदे के बारे में कुछ उदाहरण देता है। कार्यान्वयन के एक अच्छे उदाहरण के लिए, मेरे द्वारा जुड़े लेखों की जाँच करें।
कोयोट

जवाबों:


12

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

इस तरह से बहुत सारी राय है, लेकिन अगर आपका इंजन कोड अभी भी शुरुआती चरण में है, तो आपको यह समझाना मुश्किल होगा कि यह खराब क्यों हो सकता है।

सामान्य तौर पर, स्कॉट मेयर्स के पास विरासत पर कहने के लिए कुछ महान चीजें हैं। यदि आप अभी भी विकास में उस स्तर पर हैं, तो पढ़ें तो जारी रखने से पहले इस पुस्तक (प्रभावी C ++) को । यह एक शानदार पुस्तक है, और हमारी कंपनी में काम करने वाले किसी भी कोडर के लिए एक आवश्यकता है। लेकिन सलाह देने के लिए: यदि आप एक वर्ग लिख रहे हैं और विरासत का उपयोग करने पर विचार कर रहे हैं, तो बिल्कुल स्पष्ट हो कि कक्षा और उसके पूर्वज के बीच का संबंध एक 'एक' संबंध है। यही है, हर बी एक ए है। कार्यक्षमता ए प्रदान करने के लिए बी एक्सेस देने के लिए एक आसान तरीका के रूप में वंशानुक्रम का उपयोग न करें, इस तरह से गंभीर वास्तु संबंधी समस्याएं हो सकती हैं, और इसे करने के अन्य तरीके भी हैं।

सीधे शब्दों में कहें; किसी भी आकार के एक इंजन के लिए, आप जल्दी से पाएंगे कि वस्तुएं शायद ही कभी एक श्रेणीबद्ध संरचना में गिरती हैं जिसके लिए वंशानुक्रम काम करता है। उचित होने पर इसका उपयोग करें, लेकिन हर चीज के लिए इसका उपयोग करने के जाल में न पड़ें - वस्तुओं को आपस में जोड़ने के अन्य तरीके सीखें।


मैं वंशानुक्रम के बारे में दूसरी राय देता हूं। अंगूठे का मेरा व्यक्तिगत नियम है: ऐसा तब तक न करें जब तक कि आपको ऐसा न करना पड़े या किसी अन्य तरीके से करना बेवकूफी न हो। दूसरे शब्दों में, 99% मामलों (कम से कम :)) में विरासत बुरा विचार है। साथ ही, गेम लॉजिक (रोटेट / मूव) को रेंडरिंग (जनरेट_मेश ()) से अलग करना अच्छा हो सकता है। एक और बात आग है () - कार्यों की एक सूची और एक सामान्य कार्रवाई (my_action) फ़ंक्शन होने पर विचार करें - इस तरह आप सभी वर्गों में फैले एक bazillion विभिन्न कार्यों के साथ समाप्त नहीं होंगे। या तो मामले में, इसे सरल और परिष्कृत रूप से रखें क्योंकि आप समस्याओं में आते हैं।
गिलियड

8

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

इकाई प्रणालियों के बारे में भी इस लेख ने मुझे प्रेरित किया जब मैंने पहली बार इसे पढ़ा।

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

घटक कई स्थितियों में बहुत आसान हैं। आप उनका उपयोग करके बहुत कुछ हासिल कर सकते हैं:

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

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

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

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

बेहतर जानकारी के लिए [घटक-आधारित] और [संस्था-प्रणाली] टैग किए गए प्रश्नों पर एक नज़र डालें ।


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