मैं घटक-से-ऑब्जेक्ट संचार को सुरक्षित रूप से और कैश-फ्रेंडली घटक भंडारण के साथ कैसे समर्थन कर सकता हूं?


9

मैं एक गेम बना रहा हूं जो घटक-आधारित गेम ऑब्जेक्ट का उपयोग करता है, और मुझे प्रत्येक घटक के लिए अपने गेम ऑब्जेक्ट के साथ संवाद करने के लिए एक कठिन तरीका लागू करना है। एक बार में सब कुछ समझाने के बजाय, मैं प्रासंगिक नमूना कोड के प्रत्येक भाग को समझाऊंगा:

class GameObjectManager {
    public:
        //Updates all the game objects
        void update(Time dt);

        //Sends a message to all game objects
        void sendMessage(Message m);

    private:
        //Vector of all the game objects
        std::vector<GameObject> gameObjects;

        //vectors of the different types of components
        std::vector<InputComponent> input;
        std::vector<PhysicsComponent> ai;
        ...
        std::vector<RenderComponent> render;
}

GameObjectManagerसभी खेल वस्तुओं और उनके घटक रखती है। यह गेम ऑब्जेक्ट्स को अपडेट करने के लिए भी जिम्मेदार है। यह एक विशिष्ट क्रम में घटक वैक्टर को अपडेट करके करता है। मैं सरणियों के बजाय वैक्टर का उपयोग करता हूं ताकि गेम ऑब्जेक्ट्स की संख्या की लगभग कोई सीमा न हो जो एक बार में मौजूद हो सकते हैं।

class GameObject {
    public:
        //Sends a message to the components in this game object
        void sendMessage(Message m);

    private:
        //id to keep track of components in the manager
        const int id;

        //Pointers to components in the game object manager
        std::vector<Component*> components;
}

GameObjectवर्ग जानता है कि क्या उसके घटकों कर रहे हैं और उन्हें संदेश भेज सकते हैं।

class Component {
    public:
        //Receives messages and acts accordingly
        virtual void handleMessage(Message m) = 0;

        virtual void update(Time dt) = 0;

    protected:
        //Calls GameObject's sendMessage
        void sendMessageToObject(Message m);

        //Calls GameObjectManager's sendMessage
        void sendMessageToWorld(Message m);
}

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

अब समस्या पर उठता है कैसे घटकों कॉल कर सकते हैं sendMessageमें काम करता है GameObjectऔर GameObjectManager। मैं दो संभावित समाधानों के साथ आया:

  1. इसके लिए Componentएक सूचक दें GameObject

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

  1. के लिए Componentएक सूचक दें GameObjectManager

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

अपने कोड को सुरक्षित और कैश के अनुकूल रखते हुए मैं इस समस्या को कैसे हल कर सकता हूं?

जवाबों:


6

आपका संचार मॉडल ठीक लगता है, और विकल्प एक ठीक काम करेगा यदि आप केवल उन बिंदुओं को सुरक्षित रूप से संग्रहीत कर सकते हैं। आप घटक भंडारण के लिए एक अलग डेटा संरचना चुनकर उस समस्या को हल कर सकते हैं।

A std::vector<T>एक उचित पहली पसंद थी। हालाँकि, कंटेनर का इटेरेटर अमान्य व्यवहार एक समस्या है। आप जो चाहते हैं, वह एक डेटा संरचना है जो तेजी से और कैश-सुसंगत से अधिक है, और जो आइटम सम्मिलित करते या निकालते समय पुनरावृत्ति स्थिरता को भी बनाए रखता है।

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

दूसरे शब्दों में, वैचारिक रूप से कुछ ऐसा है:

struct Page {
   int count;
   int capacity;           // Optional if every page is a fixed size.
   T * m_storage;
   bool * m_skip;          // Skip list; can be bit-compressed.
   std::stack<int> m_free; // Can be replaced with a specialized stack.

   Page * next;
   Page * prior;           // Optional, allows reverse iteration
};

मैं अकल्पनीय रूप से इस डेटा संरचना को एक पुस्तक कहता हूं (क्योंकि यह उन पृष्ठों का संग्रह है जो आपके द्वारा पुनरावृति करते हैं), लेकिन संरचना के विभिन्न नाम हैं।

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

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

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

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


नमस्ते! क्या कोई संसाधन है जहां मैं "कॉलोनी" के विकल्प के बारे में अधिक जान सकता हूं जिसका आपने पिछले पैराग्राफ में उल्लेख किया था? क्या वे कहीं भी लागू किए गए हैं? मैं कुछ समय से इस विषय पर शोध कर रहा हूं और मुझे वास्तव में दिलचस्पी है।
रिनैट वेलियाखेमदोव

5

'कैश फ्रेंडली' होना एक बड़ा खेल है। यह मेरे लिए समयपूर्व अनुकूलन प्रतीत होता है।


'कैश फ्रेंडली' होने के बिना इसे हल करने का एक तरीका ढेर के बजाय ढेर पर अपनी वस्तु बनाना होगा: newअपनी वस्तुओं के लिए उपयोग और (स्मार्ट) पॉइंटर्स। इस तरह, आप अपनी वस्तुओं को संदर्भित कर पाएंगे और उनका संदर्भ अमान्य नहीं होगा।

अधिक कैश फ्रेंडली समाधान के लिए, आप स्वयं ऑब्जेक्ट्स के डी / आवंटन का प्रबंधन कर सकते हैं, और इन ऑब्जेक्ट्स को हैंडल का उपयोग कर सकते हैं।

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

जैसा कि आपको घटक की आवश्यकता है, आप मेमन को इस ऑब्जेक्ट तक पहुंचने के लिए कहेंगे, जो यह खुशी से करेगा। लेकिन इसका संदर्भ न रखें क्योंकि ...।

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

पाठ्यपुस्तकों का कहना है कि आपकी स्मृति के प्रबंधन के इस तरीके के कम से कम 2 फायदे हैं:

  1. कम कैश की कमी होती है क्योंकि ऑब्जेक्ट मेमोरी में एक दूसरे के करीब होते हैं और
  2. यह आपके द्वारा ओएस पर किए जाने वाले मेमोरी डे / एलोकेशन कॉल की संख्या को कम कर देता है, जो कुछ समय लेने के लिए कहा जाता है ।

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

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