रेंडरिंग से गेम डेटा / लॉजिक को अलग करना


21

मैं C ++ और OpenGL 2.1 का उपयोग करके एक गेम लिख रहा हूं। मैं सोच रहा था कि मैं डेटा / तर्क को प्रतिपादन से अलग कैसे कर सकता हूं। फिलहाल मैं एक बेस क्लास 'रेंडरटेबल' का इस्तेमाल करता हूं जो ड्राइंग को लागू करने के लिए एक शुद्ध वर्चुअल तरीका देता है। लेकिन प्रत्येक ऑब्जेक्ट में इतना विशिष्ट कोड होता है, केवल ऑब्जेक्ट जानता है कि शेडर वर्दी को ठीक से कैसे सेट किया जाए और वर्टेक्स ऐरे बफर डेटा को व्यवस्थित किया जाए। मैं अपने सभी कोड में बहुत सारे gl * function कॉल के साथ समाप्त होता हूं। क्या वस्तुओं को खींचने का कोई सामान्य तरीका है?


4
वास्तव में अपनी वस्तु के लिए एक रेंडर करने के लिए रचना का उपयोग करें और अपनी वस्तु को उस m_renderableसदस्य के साथ बातचीत करें । इस तरह, आप अपने तर्क को बेहतर ढंग से अलग कर सकते हैं। सामान्य वस्तुओं पर रेंडर करने योग्य "इंटरफ़ेस" को लागू न करें जिसमें भौतिकी, एआई और व्हाट्सन भी हैं। उसके बाद, आप अलग से रेंडर प्रबंधन कर सकते हैं। आप चीजों को और भी अधिक खराब करने के लिए OpenGL फ़ंक्शन कॉल पर अमूर्तता की एक परत की आवश्यकता होती है। इसलिए, एक अच्छे इंजन की अपेक्षा न करें कि उसके विभिन्न रेंडर करने योग्य कार्यान्वयन के अंदर कोई GL API कॉल हो। यह एक सूक्ष्म संक्षेप में है।
तियोड्रोन

1
@teodron: आपने उसे उत्तर के रूप में क्यों नहीं रखा?
तापियो

1
@ टैपियो: क्योंकि यह एक जवाब के ज्यादा नहीं है; यह एक सुझाव के बजाय अधिक है।
टेओड्रन

जवाबों:


20

एक विचार आगंतुक डिजाइन पैटर्न का उपयोग करना है। आपको एक रेंडरर कार्यान्वयन की आवश्यकता है जो जानता है कि कैसे सहारा प्रदान करना है। रेंडर की गई नौकरी को संभालने के लिए हर ऑब्जेक्ट रेंडरर इंस्टेंस को कॉल कर सकता है।

छद्मकोश की कुछ पंक्तियों में:

class Renderer {
public:
    void render( const ObjectA & obj );
    void render( const ObjectB & obj );
};


class ObjectA{
public:
    void draw( Renderer & r ){ r.render( *this ) };
}

class ObjectB{
public:
    void draw( Renderer & r ){ r.render( *this ) };
}

Gl * सामान रेंडरर के तरीकों द्वारा कार्यान्वित किया जाता है, और ऑब्जेक्ट केवल रेंडर किए जाने के लिए आवश्यक डेटा स्टोर करते हैं, स्थिति, बनावट प्रकार, आकार ... आदि।

इसके अलावा, आप अलग-अलग रेंडरर्स (डिबग रेंडरर, hqRenderer, ... आदि) को सेटअप कर सकते हैं और वस्तुओं को बदले बिना, गतिशील रूप से इनका उपयोग कर सकते हैं।

यह भी इकाई / घटक प्रणालियों के साथ आसान हो सकता है।


1
यह एक अच्छा जवाब है! आप Entity/Componentविकल्प पर थोड़ा अधिक जोर दे सकते थे क्योंकि यह अन्य इंजन भागों (एआई, भौतिकी, नेटवर्किंग या सामान्य गेमप्ले) से अलग ज्यामिति प्रदाताओं की मदद कर सकता है। +1!
तियोड्रोन

1
@teodron, मैं E / C विकल्प की व्याख्या नहीं करूंगा क्योंकि यह चीजों की शिकायत करेगा। लेकिन, मुझे लगता है कि आपको बदलना चाहिए ObjectAऔर ObjectBप्रति DrawableComponentAऔर DrawableComponentB, और रेंडर करने के तरीकों के अंदर, अन्य घटकों का उपयोग करें यदि आपको इसकी आवश्यकता है, जैसे: position = component->getComponent("Position");और मुख्य लूप में, आपके पास ड्रा करने के लिए कॉल करने योग्य घटकों की एक सूची है।
जेन

केवल एक इंटरफ़ेस (जैसे Renderable) क्यों नहीं है जिसमें एक draw(Renderer&)फ़ंक्शन है और सभी ऑब्जेक्ट्स जो उन्हें प्रदान किए जा सकते हैं? किस मामले में Rendererकेवल एक फ़ंक्शन की आवश्यकता होती है जो किसी भी ऑब्जेक्ट को स्वीकार करता है जो सामान्य इंटरफ़ेस और कॉल को लागू करता है renderable.draw(*this);?
वेट फाल्कन

1
@ViteFalcon, क्षमा करें, अगर मैं मुझे स्पष्ट नहीं करता, लेकिन विस्तृत विवरण के लिए, मुझे और अधिक स्थान और कोड चाहिए। मूल रूप से, मेरा समाधान gl_*रेंडरर में कार्य को स्थानांतरित करता है (तर्क से तर्क को अलग करता है), लेकिन आपका समाधान gl_*ऑब्जेक्ट्स में कॉल को स्थानांतरित करता है।
जेन

इस तरह से gl * फ़ंक्शंस वास्तव में ऑब्जेक्ट कोड से बाहर चले गए हैं, लेकिन मैं अभी भी रेंडर में उपयोग किए जाने वाले हैंडल वेरिएबल को संभालता हूं, जैसे कि बफर / टेक्सचर आईडी, यूनिफॉर्म / एटिट्यूड लोकेशन।
felipe

4

मुझे पता है कि आप जेन के जवाब को पहले ही स्वीकार कर चुके हैं, लेकिन मैं किसी और मामले में मदद करना चाहता हूं।

समस्या को दोहराने के लिए, ओपी प्रतिपादन कोड को तर्क और डेटा से अलग रखने की क्षमता चाहता है।

मेरा समाधान घटक को प्रस्तुत करने के लिए एक साथ एक अलग वर्ग का उपयोग करना है, जो कि Rendererऔर तर्क वर्ग से अलग है । पहले एक Renderableइंटरफ़ेस होना चाहिए जिसमें एक फ़ंक्शन होता है bool render(Renderer& renderer);और Rendererवर्ग सभी Renderableउदाहरणों को पुनः प्राप्त करने के लिए विज़िटर पैटर्न का उपयोग करता है , GameObjectएस की सूची को देखते हुए और उन वस्तुओं को प्रदान करता है जिनके पास एक Renderableउदाहरण है। इस तरह, रेंडरर को प्रत्येक वस्तु-प्रकार के बारे में जानने की ज़रूरत नहीं है और यह अभी भी फ़ंक्शन के Renderableमाध्यम से सूचित करने के लिए प्रत्येक ऑब्जेक्ट-प्रकार की ज़िम्मेदारी है getRenderable()। या वैकल्पिक रूप से, आप एक ऐसा RenderableVisitorवर्ग बना सकते हैं जो सभी GameObjects का दौरा करता है और व्यक्तिगत GameObjectस्थिति के आधार पर वे आगंतुक को अपने रेंडर करने योग्य जोड़ने / जोड़ने के लिए चुन सकते हैं। किसी भी तरह से, मुख्य सार यह है किgl_*कॉल सभी ऑब्जेक्ट के बाहर ही होते हैं और एक ऐसे वर्ग में रहते हैं जो ऑब्जेक्ट का अंतरंग विवरण जानता है, इसके बजाय इसका हिस्सा है Renderer

अस्वीकरण : मैंने इन कक्षाओं को संपादक में हाथ से लिखा है इसलिए एक अच्छा मौका है कि मैंने कोड में कुछ याद किया है, लेकिन उम्मीद है, आपको यह विचार मिलेगा।

एक (आंशिक) उदाहरण दिखाने के लिए:

Renderable इंटरफेस

class Renderable {
public:
    Renderable(){}
    virtual ~Renderable(){}
    virtual void render(Renderer& renderer) const = 0;
};

GameObject वर्ग:

class GameObject {
public:
    GameObject()
        : mVisible(true)
        , mMarkedForDelete(false) {}

    virtual ~GameObject(){}

    virtual Renderable* getRenderable() {
        // By default, all GameObjects are missing their Renderable
        return NULL;
    }

    void setVisible(bool visible) {
        mVisible = visible;
    }

    bool isVisible() const {
        return getRenderable() != null && !isMarkedForDeletion() && mVisible;
    }

    void markForDeletion() {
        mMarkedForDelete = true;
    }

    bool isMarkedForDeletion() const {
        return mMarkedForDelete;
    }

    // More GameObject functions

private:
    bool mVisible;
    bool mMarkedForDelete;
};

(आंशिक) Renderer वर्ग।

class Renderer {
public:
    void renderObjects(std::vector<GameObject>& gameObjects) {
        // If you want to do something fancy with the renderable GameObjects,
        // create a visitor class to return the list of GameObjects that
        // are visible instead of rendering them straight-away
        std::list<GameObject>::iterator itr = gameObjects.begin(), end = gameObjects.end();
        while (itr != end) {
            GameObject* gameObject = *itr++;
            if (gameObject == null || !gameObject->isVisible()) {
                continue;
            }
            gameObject->getRenderable()->render(*this);
        }
    }

};

RenderableObject वर्ग:

template <typename T>
class RenderableObject : public Renderable {
public:
    RenderableObject(T& object)
        :mObject(object) {}
    virtual ~RenderableObject(){}

    virtual void render(Renderer& renderer) {
        return render(renderer, mObject);
    }

protected:
    virtual void render(Renderer& renderer, T& object) = 0;
};

ObjectA वर्ग:

// Forward delcare ObjectARenderable and make sure the constructor
// definition in the CPP file where ObjectARenderable gets included
class ObjectARenderable;

class ObjectA : public GameObject {
public:
    ObjectA()
        : mRenderable(new ObjectARenderable(*this)) {}

    // All data/logic

    Renderable* getRenderable() {
        return mRenderable.get();
    }

protected:
    // boost or std shared_ptr to make sure that the renderable instance is
    // cleaned up with the destruction of this object.
    shared_ptr<Renderable> mRenderable;
};

ObjectARenderable वर्ग:

#include "ObjectA.h"

class ObjectARenderable : public RenderableObject<ObjectA> {
public:
    ObjectARenderable(ObjectA& instance) {
        : RenderableObject<ObjectA>(instance) {}

protected:
    virtual void render(Renderer& renderer, T& object) {
        // gl_* class to render ObjectA
    }
};

4

एक रेंडरिंग-कमांड सिस्टम बनाएं। एक उच्च-स्तरीय वस्तु, जिसकी पहुँच OpenGLRendererऔर दृश्यपट / गेमबॉजेक्ट, दोनों तक पहुँच होती है, दृश्य ग्राफ या गेमबॉजेक्ट्स को पुन: व्यवस्थित करेगा और एक बैच का निर्माण RenderCmdsकरेगा, जिसे तब सबमिट किया जाएगा, जो OpenGLRendererप्रत्येक को बदले में खींचेगा , और इस तरह सभी OpenGL के साथ उसमें संबंधित कोड।

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

class OpenGLRenderer
{
public:
    typedef GLuint GeometryBuffer;
    typedef GLuint TextureID;
    typedef std::vector<RenderCmd> RenderBatch; 

    void Render(const RenderBatch& renderBatch);   // set shaders, set active textures, draw geometry, ...

    MeshID CreateGeometryBuffer(...);
    TextureID CreateTexture(...);

    // ....
}

struct RenderCmd
{
    GeometryBuffer mGeometryBuffer;
    TextureID mTexture;
    Mat4& mWorldMatrix;
    bool mLightingEnabled;
    // .....
}

std::vector<GameObject> gYourGameObjects;
RenderBatch BuildRenderBatch()
{
    RenderBatch ret;

    for (GameObject& object : gYourGameObjects)
    { 
        // ....
    }

    return ret;
}

3

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


1
मौसम = बारिश, सूरज, गर्म, ठंडा: पी ->
विकर

3
@TobiasKienzler अगर आप उसकी स्पेलिंग को सही करने जा रहे हैं, तो स्पेल करने की कोशिश करें कि क्या सही ढंग से :-)
TASagent


1
सही है कि टाइपो
दानियार

2

निश्चित रूप से रेंडरिंग कोड और गेम लॉजिक को विभिन्न वर्गों में रखा गया है। रचना (जैसा कि टेओड्रोन ने सुझाव दिया है) संभवतः ऐसा करने का सबसे अच्छा तरीका है; खेल की दुनिया में प्रत्येक इकाई का अपना रेंडर करने योग्य होगा - या शायद उनमें से एक सेट।

आपके पास अभी भी रेंडरटेबल के कई उपवर्ग हो सकते हैं, उदाहरण के लिए कंकाल एनीमेशन, कण उत्सर्जक, और जटिल शेडर्स को संभालने के लिए, आपके मूल बनावट-और-छायादार के अलावा। रेंडर करने योग्य वर्ग और उसके उपवर्गों में केवल रेंडरिंग के लिए आवश्यक जानकारी होनी चाहिए: ज्यामिति, बनावट और शेड्स।

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

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


2

यह सलाह वास्तव में प्रतिपादन के लिए विशिष्ट नहीं है, लेकिन ऐसी प्रणाली के साथ आने में मदद करनी चाहिए जो चीजों को काफी हद तक अलग रखती है। सबसे पहले 'गेमऑब्जेक्ट' डेटा को स्थिति की जानकारी से अलग रखने की कोशिश करें।

यह ध्यान देने योग्य है कि साधारण XYZ स्थिति जानकारी इतनी सरल नहीं हो सकती है। यदि आप एक भौतिक विज्ञान इंजन का उपयोग कर रहे हैं तो आप स्थिति डेटा को 3 पार्टी इंजन में संग्रहीत कर सकते हैं। आपको या तो उनके बीच तालमेल बैठाना होगा (जिसमें बहुत सारी व्यर्थ मेमोरी कॉपी करना शामिल होगा) या इंजन से सीधे सूचना को क्वेरी करना होगा। लेकिन सभी वस्तुओं को भौतिक विज्ञान की आवश्यकता नहीं है, कुछ को जगह में तय किया जाएगा ताकि तैरने का एक सरल सेट वहां ठीक काम करे। कुछ अन्य वस्तुओं से भी जुड़े हो सकते हैं, इसलिए उनकी स्थिति वास्तव में किसी अन्य स्थिति की भरपाई है। एक उन्नत सेटअप में आपको स्थिति केवल GPU पर संग्रहीत हो सकती है, केवल उसी समय इसकी आवश्यकता होगी जब कंप्यूटर पक्ष स्क्रिप्टिंग, भंडारण और नेटवर्क प्रतिकृति के लिए होता है। तो आप अपने संभावित डेटा के लिए कई संभावित विकल्प होंगे। यहाँ यह विरासत का उपयोग करने के लिए समझ में आता है।

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

यह अच्छी मेमोरी मैनेजमेंट देने में भी मदद करता है। इस तरह से एक वस्तु जो वास्तव में एक क्षेत्र में नहीं है, उसमें एक स्थिति भी नहीं है जो 0.0 कोर्ड्स या उन कॉर्ड्स को वापस करने के बजाय समझ में आता है जो एक क्षेत्र में आखिरी बार थे।

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

इससे बचने के लिए मैं शायद एक विशेष 'लिंक' क्लास बनाऊंगा। एक जो एक स्तर और एक वस्तु के बीच बांधता है। मैं इसे "स्थान" कहता हूं। इसमें xyz निर्देशांक के साथ-साथ स्तर को संभालना और ऑब्जेक्ट को हैंडल करना शामिल होगा। इस लिंक क्लास को स्पेसियल स्ट्रक्चर / लेवल में स्टोर किया जाएगा और ऑब्जेक्ट का इसमें कमजोर रेफरेंस होगा (यदि लेवल / लोकेशन नष्ट हो जाए तो ऑब्जेक्ट्स रिफ्रेंस को null करने के लिए अपडेट करना होगा। यह लोकेशन क्लास के वास्तव में होने के लायक भी हो सकता है। ऑब्जेक्ट 'स्वयं', इस तरह से यदि कोई स्तर हटा दिया जाता है, तो विशेष इंडेक्स संरचना, इसमें शामिल स्थान और इसके ऑब्जेक्ट हैं।

typedef std::tuple<Level, Object, PositionXYZ> Location;

अब स्थिति की जानकारी केवल एक ही स्थान पर संग्रहीत है। ऑब्जेक्ट, स्पेसियल इंडेक्सिंग संरचना, रेंडरर और इतने पर के बीच दोहराव नहीं।

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

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

एक अन्य लाभ यह है कि स्थिति और स्तर के प्रतिशोध उसी स्थान पर संग्रहीत किया जाता है। आप object.TeleportTo (other_object) को लागू कर सकते हैं और यह स्तरों भर में काम कर सकता है। इसी तरह एआई पथ-खोज एक अलग क्षेत्र में कुछ का पालन कर सकता है।

प्रतिपादन के संबंध में। आपके रेंडर स्थान के लिए एक समान बाध्यकारी हो सकते हैं। सिवाय इसके वहाँ में प्रतिपादन विशिष्ट सामान होगा। संभवतः आपको इस संरचना में संग्रहित होने के लिए 'ऑब्जेक्ट' या 'स्तर' की आवश्यकता नहीं है। ऑब्जेक्ट उपयोगी हो सकता है यदि आप रंग उठाने जैसे कुछ करने की कोशिश कर रहे हैं, या इसके ऊपर तैरने वाला एक हिटबार रेंडर कर रहा है, लेकिन अन्यथा रेंडरर केवल मेष और इस तरह की परवाह करता है। RenderableStuff एक जाल होगा, इसमें बाउंडिंग बॉक्स आदि भी हो सकते हैं।

typedef std::pair<RenderableStuff, PositionXYZ> RenderThing;

renderer.render(level, camera);
renderer: object = level.getVisibleObjects(camera);
level: physics.getObjectsInArea(physics.getCameraFrustrum(camera));
for(object in objects) {
    //This could be depth sorted, meshes could be broken up and sorted by material for batch rendering or whatever
    rendering_que.addObjectToRender(object);
}

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

आप भौतिकी इंजन में एक समान अमूर्तता हो सकती है, क्योंकि इसमें ऑब्जेक्ट डेटा, बस टकराव जाल और भौतिकी गुणों की भी आवश्यकता नहीं होती है।

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

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

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