2009-09-22 13 views
9

मैं निम्नलिखित कोड मिल गया है, ग में लगता है कि सरल शूटर ++:गेम ऑब्जेक्ट पदानुक्रम मॉडल करने का सबसे सुरुचिपूर्ण और कुशल तरीका क्या है? (डिजाइन परेशान)

इस के साथ
// world.hpp 
//---------- 
class Enemy; 
class Bullet; 
class Player; 

struct World 
{ 
    // has-a collision map 
    // has-a list of Enemies 
    // has-a list of Bullets 
    // has-a pointer to a player 
}; 

// object.hpp 
//----------- 
#include "world.hpp" 

struct Object 
{ 
    virtual ~Object(); 
    virtual void Update() =0; 
    virtual void Render() const =0; 

    Float xPos, yPos, xVel, yVel, radius; // etc. 
}; 

struct Enemy: public Object 
{ 
    virtual ~Enemy(); 
    virtual void Update(); 
    virtual void Render() const; 
}; 

// Bullet and Player are similar (they render and update differently per se, 
/// but the behavior exposed to World is similar) 

// world.cpp 
//---------- 
#include "object.hpp" 

// object.cpp 
//----------- 
#include "object.hpp" 

दो समस्याओं:

1, खेल वस्तुओं अन्य खेल वस्तुओं के बारे में जानते हुए भी।

ऐसी सभी सुविधाएं हैं जो सभी वस्तुओं के बारे में जानती हैं। यह सभी ऑब्जेक्ट्स का पर्दाफाश करना पड़ सकता है या नहीं, इसे एक्वाइयर (स्थिति, एक के लिए) के मानकों के आधार पर इसमें से कुछ को बेनकाब करना होगा। यह सुविधा दुनिया माना जाता है।

ऑब्जेक्ट्स को टकराव की जानकारी और अन्य वस्तुओं के लिए पूछने के लिए, वे दुनिया के बारे में जानना है।

यह एक निर्भरता पेश करता है जहां ऑब्जेक्ट्स और विश्व के कार्यान्वयन दोनों को ऑब्जेक्ट के शीर्षलेख तक पहुंच प्राप्त होती है, इस प्रकार दुनिया में object.hpp (जो बदले में world.hpp शामिल है) के बजाय सीधे अपना हेडर शामिल नहीं करेगा। यह मुझे असहज महसूस करता है - मुझे नहीं लगता कि world.cpp को ऑब्जेक्ट में बदलाव करने के बाद recompile करना चाहिए.hpp। दुनिया ऐसा महसूस नहीं करती है कि इसे ऑब्जेक्ट के साथ काम करना चाहिए। यह खराब डिजाइन की तरह लगता है - इसे कैसे ठीक किया जा सकता है?

2, पॉलिमॉर्फिज्म - कोड पुन: उपयोग और गेम इकाइयों के तार्किक समूह (और कैसे) से परे किसी भी चीज़ के लिए इसका उपयोग किया जाना चाहिए?

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

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

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

उत्तर

5

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

एक Object से जानकारी छिपाई जा रही है के बारे में सभी वस्तुओं एक अच्छा विचार है, और के बारे में किसी भीObject जानकारी छुपा के साथ भ्रमित नहीं होना चाहिए। लोगों के संदर्भ में सोचें - आपके लिए कई अलग-अलग लोगों के बारे में जानकारी जानना और बनाए रखना उचित है, हालांकि आप उन्हें केवल उन सूचनाओं का सामना कर सकते हैं या किसी और को उनके बारे में बता रहे हैं।

फिर से संपादित करें:

ठीक है। यह वास्तव में मुझे स्पष्ट है कि आप वास्तव में क्या चाहते हैं, और यह एकाधिक प्रेषण है - फ़ंक्शन कॉल के लिए कई पैरामीटर के प्रकारों पर पॉलिमॉर्फिक रूप से स्थिति को संभालने की क्षमता, केवल एक के बजाय। दुर्भाग्यवश C++ मूल रूप से एकाधिक प्रेषण का समर्थन नहीं करता है।

यहां दो विकल्प हैं। आप डबल प्रेषण या विज़िटर पैटर्न के साथ एकाधिक प्रेषण को पुन: पेश करने का प्रयास कर सकते हैं, या आप dynamic_cast का उपयोग कर सकते हैं। जो आप उपयोग करना चाहते हैं वह परिस्थितियों पर निर्भर करता है। यदि आपके पास इसका उपयोग करने के लिए कई अलग-अलग प्रकार हैं, तो dynamic_cast शायद बेहतर दृष्टिकोण है। यदि आपके पास केवल कुछ हैं, डबल प्रेषण या विज़िटर पैटर्न शायद अधिक उपयुक्त है।

+0

के लिए धन्यवाद पहले दो पैराग्राफ; यह बहुत समझ में आता है और मेरे सिर को साफ़ करने में मदद करता है। मैंने पॉलिमॉर्फिज्म के साथ क्या करना चाहता था उसे स्पष्ट करने के लिए अपने प्रश्न को फिर से लिखा है। – zyndor

+0

विज़िटर पैटर्न अद्भुत है, इसे इंगित करने के लिए धन्यवाद। मुझे लगता है कि OnHit() विधियों पैटर्न और ऑब्जेक्ट्स की विज़िट() विधि के समान हैं, उनके स्वयं के विज़िटर होंगे, है ना? – zyndor

+1

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

6

मुझे लगता है कि आप गेम ऑब्जेक्ट में दुनिया का संदर्भ देना चाहते हैं। यह Dependency Injection का एक सुंदर मूल सिद्धांत है। टकराव के लिए, दुनिया Strategy pattern का उपयोग यह हल करने के लिए कर सकती है कि विशिष्ट प्रकार के ऑब्जेक्ट कैसे इंटरैक्ट करते हैं।

यह प्राथमिक वस्तुओं से अलग ऑब्जेक्ट प्रकारों का ज्ञान रखता है और बातचीत के लिए विशिष्ट ज्ञान वाले वस्तुओं में इसे समाहित करता है।

+0

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

+0

आप कैसे विश्व वस्तु के संदर्भ को संग्रहीत करते हैं आप पर निर्भर है। चूंकि इन ऑब्जेक्ट्स को कई विश्वों में एक साथ तत्काल नहीं किया जाएगा, इसलिए कक्षा पर संदर्भ संग्रहीत करना शायद ठीक है। मुख्य विचार यह है कि दुनिया को तत्काल समय पर अलग-अलग वस्तुओं को प्रदान किया जाता है, इसलिए यदि आप अलग-अलग दुनिया में एक ही प्रकार की दो वस्तुएं रखना चाहते हैं, तो उदाहरण API एपीआई नहीं बदलता है और केवल एक ही रिफैक्टरिंग जो होने की आवश्यकता है वस्तु के कार्यान्वयन के भीतर ही है। –

3

वस्तुओं, विश्व वे शामिल हों बारे में जानने की टक्कर जानकारी और अन्य वस्तुओं के लिए क्वेरी करने के लिए किया है।

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

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

0

यह बहुत सोचा देने के बाद मैंने महसूस किया कि हालांकि वस्तुओं DO एक दुनिया के लिए उपयोग किया है जरूरत है, यह विश्व की वस्तुओं के लिए पास वस्तुओं को सर्व करने की जिम्मेदारी नहीं है।

यही वह है जो मेरे पास एरिना गैर-ऑब्जेक्ट था (कभी भी तत्काल नहीं + सभी स्थिर सदस्यों में, आवश्यक वस्तु श्रेणियों की सूची - गोलियां, दुश्मन, दृश्य इत्यादि शामिल हैं।

// object.hpp 
//----------- 
#include "world.hpp" 

// NOTE: the obvious virtual and pure virtual methods are omitted in the following code 
class Object 
{...}; 

class Enemy: public Object 
{...}; 

class Bullet: public Object 
{...}; 

class Player: public Object 
{...}; 

// arena.hpp 
//----------- 
#include "object.hpp" 

struct Arena 
{ 
    // has-a lists of object categories and a (pointer to a) player 
} 

// object.cpp 
//----------- 
#include "arena.hpp" // for the damn dependencies 

// arena.cpp 
//----------- 
#include "arena.hpp" 

तो, समाधान, या क्या ऐसा लगता है:) के लिए, लेकिन यह एक ऐसी ही निर्भरता संरचना (विश्व के हिस्से के रूप श्रेणी सूचियों होने जिसके कारण यह) एक अच्छा समाधान की तरह महसूस नहीं किया था के रूप में पेश इस बिंदु पर, ऑब्जेक्ट की पूर्णता (श्रेणी) वस्तुओं की घोषणा के ऊपर या नीचे एक संकलन स्तर नहीं है, लेकिन उसी स्तर पर पर है।

// object.hpp 
//----------- 
#include "world.hpp" 

class Object 
{ 
    static World *pWorld; 
    ... 
}; 

class Enemy: public Object 
{ 
    typedef std::list<Enemy*> InstList; 
    static InstList insts; 
    ... 
}; 

class Bullet: public Object 
{ 
    typedef std::list<Bullet*> InstList; 
    static InstList insts; 
    ... 
}; 

class Player: public Object 
{ 
    static Player *pThePlayer; 
    ... 
}; 

// object.cpp 
//----------- 
#include "object.hpp" 

यहां तक ​​कि अगर वहाँ 'श्रेणियों सूचियों उनके लिए पूरी तरह से उपलब्ध हैं विशेषज्ञता दुश्मन, गोलियों आदि उनके (और अन्य)' के लिए आगे हेडर object.hpp जो वे स्पष्ट रूप से वैसे भी करना है को शामिल करके कर रहे हैं।

पॉलिमॉर्फिज्म बिट के बारे में और विभिन्न श्रेणियों को अलग-अलग सूचियों में क्यों रखा जाता है: ऑब्जेक्ट श्रेणियों के बेस क्लास (बुलेट, दुश्मन, प्लेयर) कुछ प्रकार की वस्तुओं को मारने के लिए "ईवेंट हैंडलर" प्रदान कर सकते हैं; हालांकि, उन्हें श्रेणी स्तर की बजाय ऑब्जेक्ट स्तर पर घोषित नहीं किया गया है। (उदाहरण के लिए हम बुलेट बनाम-गोली टकराव के बारे में परवाह नहीं करते हैं, वे जाँच नहीं कर रहे हैं और नहीं संभाला।)

// object.hpp 
//----------- 
#include "world.hpp" 

class Object 
{ 
    static World *pWorld; 
    ... 
}; 

class Bullet: public Object 
{ 
    typedef std::list<Bullet*> InstList; 
    static InstList insts; 
    ... 
}; 

class Player: public Object 
{ 
    static Player *pThePlayer; 

    void OnHitBullet(const Bullet *b); 
    ... 
}; 

class Enemy: public Object 
{ 
    typedef std::list<Enemy*> InstList; 
    static InstList insts; 

    virtual void OnHitBullet(const Bullet *b); 
    virtual void OnHitPlayer(const Player *p); 
    ... 
}; 

संपादित करें, पूर्णता के लिए 'खातिर:

// game.hpp 
//----------- 
#include "object.hpp" 

struct Game 
{ 
    static World world; 

    static void Update(); // update world and objects, check for collisions and 
         /// handle them. 
    static void Render(); 
}; 
संबंधित मुद्दे

 संबंधित मुद्दे