मैं एक शूटर (जैसे 1942, क्लासिक 2 डी ग्राफिक्स) लिख रहा हूं और मैं एक घटक आधारित दृष्टिकोण का उपयोग करना चाहता हूं। अब तक मैंने निम्नलिखित डिज़ाइन के बारे में सोचा:
प्रत्येक खेल तत्व (एयरशिप, प्रोजेक्टाइल, पावरअप, दुश्मन) एक इकाई है
प्रत्येक इकाई घटकों का एक समूह है जिसे रन-टाइम पर जोड़ा या हटाया जा सकता है। उदाहरण स्थिति, स्प्राइट, स्वास्थ्य, आईए, नुकसान, बाउंडिंगबॉक्स आदि हैं।
यह विचार है कि Airship, Projectile, Enemy, Powerup खेल वर्ग नहीं हैं। एक इकाई केवल उन घटकों द्वारा परिभाषित की जाती है जो इसके मालिक हैं (और जो समय के दौरान बदल सकते हैं)। तो खिलाड़ी Airship स्प्राइट, स्थिति, स्वास्थ्य और इनपुट घटकों के साथ शुरू होता है। पॉवरअप में स्प्राइट, पोजिशन, बाउंडिंगबॉक्स होता है। और इसी तरह।
मुख्य लूप गेम "भौतिकी" का प्रबंधन करता है, अर्थात घटक एक दूसरे से कैसे बातचीत करते हैं:
foreach(entity (let it be entity1) with a Damage component)
foreach(entity (let it be entity2) with a Health component)
if(the entity1.BoundingBox collides with entity2.BoundingBox)
{
entity2.Health.decrease(entity1.Damage.amount());
}
foreach(entity with a IA component)
entity.IA.update();
foreach(entity with a Sprite component)
draw(entity.Sprite.surface());
...
घटक मुख्य C ++ एप्लिकेशन में हार्डकोड किए गए हैं। एक्सएमएल फ़ाइल में एंटिटीज़ को परिभाषित किया जा सकता है (एक लुआ या पायथन फ़ाइल में आईए हिस्सा)।
मुख्य लूप संस्थाओं के बारे में बहुत परवाह नहीं करता है: यह केवल घटकों का प्रबंधन करता है। सॉफ्टवेयर डिज़ाइन को इसकी अनुमति देनी चाहिए:
एक घटक को देखते हुए, वह इकाई प्राप्त करें जिसका वह संबंधित है
एक इकाई को देखते हुए, "टाइप" का घटक प्राप्त करें
सभी संस्थाओं के लिए, कुछ करो
सभी इकाई के घटक के लिए, कुछ करें (जैसे: क्रमबद्ध करें)
मैं निम्नलिखित के बारे में सोच रहा था:
class Entity;
class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
class Sprite : public Component {...};
class Position : public Component {...};
class IA : public Component {... virtual void update() = 0; };
// I don't remember exactly the boost::fusion map syntax right now, sorry.
class Entity
{
int id; // entity id
boost::fusion::map< pair<Sprite, Sprite*>, pair<Position, Position*> > components;
template <class C> bool has_component() { return components.at<C>() != 0; }
template <class C> C* get_component() { return components.at<C>(); }
template <class C> void add_component(C* c) { components.at<C>() = c; }
template <class C> void remove_component(C* c) { components.at<C>() = 0; }
void serialize(filestream, op) { /* Serialize all componets*/ }
...
};
std::list<Entity*> entity_list;
इस डिजाइन के साथ मैं # 1, # 2, # 3 (बढ़ावा देने के लिए धन्यवाद :: फ्यूजन :: मैप एल्गोरिदम) और # 4 प्राप्त कर सकता हूं। इसके अलावा सब कुछ हे (1) है (ठीक है, बिल्कुल नहीं, लेकिन यह अभी भी बहुत तेज है)।
एक और "सामान्य" दृष्टिकोण भी है:
class Entity;
class Component { Entity* entity; ... virtual void serialize(filestream, op) = 0; ...}
class Sprite : public Component { static const int type_id = 0; };
class Position : public Component { static const int type_id = 1; };
class Entity
{
int id; // entity id
std::vector<Component*> components;
bool has_component() { return components[i] != 0; }
template <class C> C* get_component() { return dynamic_cast<C> components[C::id](); } // It's actually quite safe
...
};
इकाई वर्ग से छुटकारा पाने के लिए एक और दृष्टिकोण है: प्रत्येक घटक प्रकार अपनी सूची में रहता है। इसलिए स्प्राइट लिस्ट, हेल्थ लिस्ट, डैमेज लिस्ट आदि है। मुझे पता है कि वे एक ही लॉजिक एंटिटी की वजह से हैं। यह सरल, लेकिन धीमा है: IA घटकों को मूल रूप से सभी अन्य इकाई के घटकों तक पहुंच की आवश्यकता होती है और प्रत्येक चरण में एक दूसरे के घटक की सूची को खोजने की आवश्यकता होगी।
आपको कौन सा दृष्टिकोण बेहतर लगता है? बढ़ावा है :: फ्यूजन मैप को उस तरह से उपयोग करने के लिए अनुकूल है?