मैं अपने C ++ एंटिटी-घटक-सिस्टम में घटकों को ठीक से कैसे उपयोग कर सकता हूं?


18

(मैं जो वर्णन कर रहा हूं वह इस डिजाइन पर आधारित है: इकाई प्रणाली ढांचा क्या है?, नीचे स्क्रॉल करें और आप इसे पा लेंगे)

मुझे C ++ में एक इकाई-घटक प्रणाली बनाने में कुछ समस्याएं आ रही हैं। मेरे पास मेरा घटक वर्ग है:

class Component { /* ... */ };

जो वास्तव में एक इंटरफ़ेस है, जिसे अन्य घटकों के लिए बनाया जाना है। इसलिए, एक कस्टम घटक बनाने के लिए, मैं सिर्फ इंटरफ़ेस लागू करता हूं और उस डेटा को जोड़ता हूं जिसका उपयोग इन-गेम में किया जाएगा:

class SampleComponent : public Component { int foo, float bar ... };

इन घटकों को एक इकाई वर्ग के अंदर संग्रहीत किया जाता है, जो इकाई के प्रत्येक उदाहरण को एक अद्वितीय आईडी देता है:

class Entity {
     int ID;
     std::unordered_map<string, Component*> components;
     string getName();
     /* ... */
};

घटक के नाम को हैशिंग द्वारा इकाई में जोड़ा जाता है (यह शायद इतना बड़ा विचार नहीं है)। जब मैं एक कस्टम घटक जोड़ता हूं, तो यह एक घटक प्रकार (बेस क्लास) के रूप में संग्रहीत होता है।

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

/* System and Node implementations: (not the interfaces!) */

class SampleSystem : public System {
        std::list<SampleNode> nodes; //uses SampleNode, not Node
        void update();
        /* ... */
};

class SampleNode : public Node {
        /* Here I define which components SampleNode (and SampleSystem) "needs" */
        SampleComponent* sc;
        PhysicsComponent* pc;
        /* ... more components could go here */
};

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

जवाबों:


23

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

जैसा नक्शा घोषित किया गया है:

std::unordered_map<const std::type_info* , Component *> components;

एक addComponent फ़ंक्शन जैसे:

components[&typeid(*component)] = component;

और एक getComponent:

template <typename T>
T* getComponent()
{
    if(components.count(&typeid(T)) != 0)
    {
        return static_cast<T*>(components[&typeid(T)]);
    }
    else 
    {
        return NullComponent;
    }
}

आपको मिसकास्ट नहीं मिलेगा। ऐसा इसलिए है क्योंकि typeidघटक के रनटाइम प्रकार (सबसे अधिक व्युत्पन्न प्रकार) की जानकारी के लिए एक सूचक लौटाएगा। चूंकि यह घटक उस प्रकार की जानकारी के साथ संग्रहीत है, जैसा कि यह महत्वपूर्ण है, इसलिए कास्ट बेमेल प्रकारों के कारण मुद्दों का कारण नहीं बन सकता है। आपको टेम्प्लेट टाइप पर कंपाइल टाइम टाइप की जाँच भी करनी है क्योंकि यह कंपोनेंट से निकला हुआ टाइप है या static_cast<T*>वसीयत के साथ बेमेल टाइप होगा unordered_map

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

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


3
मैं अब लगभग 6 वर्षों से C ++ का उपयोग कर रहा हूं, फिर भी हर सप्ताह मैं कुछ नई चाल सीखता हूं।
नाइट666

जवाब के लिए धन्यवाद। मैं पहले पहली विधि का उपयोग करने की कोशिश करूंगा, और अगर बाद में शायद मैं दूसरे के उपयोग के तरीके के बारे में सोचूंगा। लेकिन, क्या addComponent()विधि को एक टेम्पलेट विधि की भी आवश्यकता नहीं होगी? यदि मैं एक परिभाषित करता हूं addComponent(Component* c), तो मेरे द्वारा जोड़ा गया कोई भी उप-घटक एक Componentपॉइंटर में संग्रहीत किया typeidजाएगा , और हमेशा Componentआधार वर्ग को संदर्भित करेगा ।
फेडेरिको

2
टाइपिड आपको वास्तविक प्रकार का ऑब्जेक्ट देगा, भले ही पॉइंटर एक बेस क्लास का हो
Chewy Gumball

मुझे वास्तव में च्वॉइस का जवाब पसंद आया, इसलिए मैंने इसे mingw32 पर लागू करने की कोशिश की। मैं फेड रिको द्वारा बताए गए मुद्दे में भाग गया जहां addComponent () सब कुछ एक घटक के रूप में संग्रहीत करता है क्योंकि टाइपिड घटक सब कुछ के लिए प्रकार के रूप में वापस आ रहा है। यहां किसी ने उल्लेख किया है कि टाइपिड को वास्तविक प्रकार का ऑब्जेक्ट दिया जाना चाहिए, भले ही पॉइंटर एक बेस क्लास के लिए हो, लेकिन मुझे लगता है कि यह संकलक के आधार पर भिन्न हो सकता है, आदि क्या कोई अन्य इसकी पुष्टि कर सकता है? मैं विंडोज 7 पर g ++ std = c ++ 11 mingw32 का उपयोग कर रहा था। मैंने एक टेम्पलेट बनने के लिए बस getComponent () को संशोधित किया, फिर उस प्रकार से इसे बचा लिया
shwoseph

यह संकलक विशिष्ट नहीं है। टाइपिड फ़ंक्शन के तर्क के रूप में आपके पास संभवतः सही अभिव्यक्ति नहीं थी।
Chewy Gumball

17

Chewy के पास यह सही है, लेकिन यदि आप C ++ 11 का उपयोग कर रहे हैं तो आपके पास कुछ नए प्रकार हैं जिनका आप उपयोग कर सकते हैं।

const std::type_info*अपने नक्शे में कुंजी के रूप में उपयोग करने के बजाय , आप std::type_index( cppreference.com देखें ) का उपयोग कर सकते हैं , जो चारों ओर एक आवरण है std::type_info। आप इसका उपयोग क्यों करेंगे? std::type_indexवास्तव में के साथ संबंध संग्रहीत करता std::type_infoसूचक के रूप में है, लेकिन उस के बारे में चिंता करने के लिए आप के लिए एक सूचक कम है।

यदि आप वास्तव में C ++ 11 का उपयोग कर रहे हैं, तो मैं Componentस्मार्ट पॉइंटर्स के अंदर संदर्भों को संग्रहीत करने की सलाह दूंगा। तो नक्शा कुछ इस तरह हो सकता है:

std::map<std::type_index, std::shared_ptr<Component> > components

एक नई प्रविष्टि जोड़ने से ऐसा किया जा सकता है:

components[std::type_index(typeid(*component))] = component

जहां componentइस प्रकार का है std::shared_ptr<Component>। किसी दिए गए प्रकार के संदर्भ को पुन Component: देख सकते हैं जैसे:

template <typename T>
std::shared_ptr<T> getComponent()
{
    std::type_index index(typeid(T));
    if(components.count(std::type_index(typeid(T)) != 0)
    {
        return static_pointer_cast<T>(components[index]);
    }
    else
    {
        return NullComponent
    }
}

के static_pointer_castबजाय के उपयोग पर भी ध्यान दें static_cast


1
मैं वास्तव में अपने स्वयं के प्रोजेक्ट में इस तरह के दृष्टिकोण का उपयोग कर रहा हूं।
विजोक

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

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

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