इकाई प्रणाली और प्रतिपादन


11

ओके, मैं अब तक क्या जानता हूं; इकाई में एक घटक (डेटा-भंडारण) होता है जो जानकारी रखता है जैसे; - बनावट / स्प्राइट - शेडर - आदि

और फिर मेरे पास एक रेंडरर सिस्टम है जो यह सब आकर्षित करता है। लेकिन मुझे समझ में नहीं आता है कि रेंडर कैसे डिज़ाइन किया जाना चाहिए। क्या मुझे प्रत्येक "दृश्य प्रकार" के लिए एक घटक होना चाहिए। Shader के बिना एक घटक, shader के साथ एक, आदि?

बस इसे करने के लिए "सही तरीका" व्हाट्स पर कुछ इनपुट की आवश्यकता है। युक्तियाँ और नुकसान के लिए बाहर देखने के लिए।


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

जवाबों:


8

यह उत्तर देने के लिए एक कठिन प्रश्न है क्योंकि सभी का अपना विचार है कि कैसे एक इकाई घटक प्रणाली को संरचित किया जाना चाहिए। जो सबसे अच्छा मैं कर सकता हूं, वह आपके साथ साझा की गई कुछ चीजें हैं जो मेरे लिए सबसे उपयोगी हैं।

सत्ता

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

public abstract class Entity {
    public bool IsAlive = true;
    public virtual SpatialComponent   Spatial   { get; set; }
    public virtual ImageComponent     Image     { get; set; }
    public virtual AnimationComponent Animation { get; set; }
    public virtual InputComponent     Input     { get; set; }
}

अवयव

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

सिस्टम

सिस्टम कम "बेवकूफ" हैं, लेकिन अभी भी गूंगा ऑटोमैटोन हैं। उनके पास समग्र प्रणाली का कोई संदर्भ नहीं है, अन्य प्रणालियों का कोई संदर्भ नहीं है और कुछ बफ़र्स को छोड़कर कोई डेटा नहीं रखता है कि उन्हें अपनी व्यक्तिगत प्रसंस्करण करने की आवश्यकता हो सकती है। सिस्टम के आधार पर, यह एक विशेष Update, या Drawविधि, या कुछ मामलों में दोनों हो सकता है।

इंटरफेस

मेरे सिस्टम में इंटरफेस एक महत्वपूर्ण संरचना है। वे क्या परिभाषित करने के लिए उपयोग किया जाता हैSystem प्रक्रिया हो सकती है, और क्या एक Entityसक्षम है। प्रतिपादन के लिए प्रासंगिक इंटरफेस हैं: IRenderableऔर IAnimatable

इंटरफेस बस उस सिस्टम को बताते हैं जो घटक उपलब्ध हैं। उदाहरण के लिए, रेंडरिंग सिस्टम को इकाई के बाउंडिंग बॉक्स और ड्रॉ करने के लिए छवि को जानना होगा। मेरे मामले में, यह वही होगाSpatialComponent और होगा ImageComponent। तो यह इस तरह दिखता है:

public interface IRenderable {
    SpatialComponent Component { get; }
    ImageComponent   Image     { get; }
}

RenderingSystem

तो रेंडरिंग सिस्टम एक इकाई कैसे तैयार करता है? यह वास्तव में काफी सरल है, इसलिए मैं आपको सिर्फ एक विचार देने के लिए छीन लिया गया वर्ग दिखाऊंगा:

public class RenderSystem {
    private SpriteBatch batch;
    public RenderSystem(SpriteBatch batch) {
        this.batch = batch;
    }
    public void Draw(List<IRenderable> list) {
        foreach(IRenderable obj in list) {
            this.batch.draw(
                obj.Image.Texture,
                obj.Spatial.Position,
                obj.Image.Source,
                Color.White);
        }
    }
}

कक्षा को देखते हुए, रेंडर सिस्टम को यह भी पता नहीं है कि क्या है Entity है। इसके बारे में सभी जानते हैं IRenderableऔर इसे आकर्षित करने के लिए बस एक सूची दी गई है।

यह सब कैसे काम करता है

यह समझने में भी मदद मिल सकती है कि मैं नई गेम ऑब्जेक्ट कैसे बना सकता हूं और सिस्टम को कैसे खिलाऊंगा।

संस्थाओं का निर्माण

सभी गेम ऑब्जेक्ट्स को इकाई से विरासत में मिला है, और कोई भी लागू इंटरफेस जो यह बताता है कि गेम ऑब्जेक्ट क्या कर सकता है। बस स्क्रीन पर एनिमेटेड के बारे में सब कुछ इस तरह दिखता है:

public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}

सिस्टम को खिलाना

मैं उन सभी संस्थाओं की सूची रखता हूं, जिन्हें खेल की दुनिया में एकल सूची में कहा जाता है List<Entity> gameObjects। प्रत्येक फ्रेम, मैं फिर उस सूची के माध्यम से झारना और इंटरफ़ेस प्रकार के आधार पर अधिक सूचियों के लिए ऑब्जेक्ट संदर्भ की प्रतिलिपि बनाता हूं, जैसे कि List<IRenderable> renderableObjects, और List<IAnimatable> animatableObjects। इस तरह, यदि विभिन्न प्रणालियों को एक ही इकाई को संसाधित करने की आवश्यकता होती है, तो वे कर सकते हैं। फिर मैं उन सूचियों को सिस्टम Updateया Drawविधियों में से हर एक को सौंपता हूं और सिस्टम को अपना काम करने देता हूं।

एनीमेशन

आप उत्सुक हो सकते हैं कि एनीमेशन सिस्टम कैसे काम करता है। मेरे मामले में आप IAnimatable इंटरफ़ेस देखना चाहते हैं:

public interface IAnimatable {
    public AnimationComponent Animation { get; }
    public ImageComponent Image         { get; set; }
}

यहां ध्यान देने योग्य बात यह है ImageComponentकि IAnimatableइंटरफ़ेस का पहलू केवल-पढ़ने के लिए नहीं है; इसका एक सेटर है

जैसा कि आप अनुमान लगा सकते हैं, एनीमेशन घटक सिर्फ एनीमेशन के बारे में डेटा रखता है; फ़्रेम की एक सूची (जो छवि घटक हैं), वर्तमान फ्रेम, प्रति सेकंड फ़्रेम की संख्या खींची जाने वाली, अंतिम फ्रेम वृद्धि के बाद बीता हुआ समय और अन्य विकल्प।

एनिमेशन सिस्टम रेंडरिंग सिस्टम और इमेज कंपोनेंट रिलेशनशिप का फायदा उठाता है। यह बस इकाई के छवि घटक को बदलता है क्योंकि यह एनीमेशन के फ्रेम को बढ़ाता है। इस तरह, एनीमेशन को अप्रत्यक्ष रूप से रेंडरिंग सिस्टम द्वारा प्रस्तुत किया जाता है।


मुझे शायद ध्यान देना चाहिए कि मैं वास्तव में नहीं जानता कि क्या यह एक इकाई-घटक प्रणाली को लोग कॉल कर रहे हैं । एक रचना-आधारित डिजाइन को लागू करने के मेरे प्रयास में, मैंने खुद को इस पैटर्न में गिरते पाया।
साइपर

दिलचस्प! मैं आपके Entity के लिए अमूर्त वर्ग के लिए उत्सुक नहीं हूँ, लेकिन IRenderable इंटरफ़ेस एक अच्छा विचार है!
जोनाथन कॉनेल

5

मैं जिस तरह की प्रणाली के बारे में बात कर रहा हूं उसे देखने के लिए यह उत्तर देखें ।

घटक में यह विवरण होना चाहिए कि क्या आकर्षित करना है और इसे कैसे निकालना है। रेंडरिंग सिस्टम उन विवरणों को लेगा और घटक द्वारा निर्दिष्ट तरीके से इकाई को आकर्षित करेगा। केवल अगर आप अलग-अलग ड्राइंग तकनीकों का उपयोग करने के लिए थे, तो आपके पास अलग-अलग शैलियों के लिए अलग-अलग घटक होंगे।


3

घटकों में तर्क को अलग करने का मुख्य कारण डेटा का एक सेट बनाना है जो एक इकाई में संयुक्त होने पर वे उपयोगी, पुन: प्रयोज्य व्यवहार का उत्पादन करते हैं। उदाहरण के लिए एक एंटिटी को एक PhysicsComponent और RenderComponent में अलग करने से समझ में आता है क्योंकि यह संभावना है कि सभी संस्थाओं में Physics नहीं होंगे और कुछ संस्थाओं में Sprite नहीं हो सकती है।

अपने प्रश्न का उत्तर देने के लिए आपको अपनी वास्तुकला को देखना होगा और अपने आप से दो प्रश्न पूछना होगा:

  1. क्या यह समझ में आता है एक बनावट के बिना एक shader है
  2. क्या बनावट को पाठ से अलग करने से मुझे कोड के दोहराव से बचने की अनुमति मिलेगी?

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

उदाहरण के लिए, भौतिक विज्ञान और ऑडियो दोनों एक ही स्थिति का उपयोग कर सकते हैं, बल्कि तब दोनों घटक डुप्लिकेट स्थिति को संग्रहीत करते हुए आप उन्हें एक स्थिति.कॉम में रिफ्लेक्टर करते हैं और इसके लिए आवश्यक है कि भौतिक विज्ञानकम्पोनेंट / ऑडियोकॉमॉपेंट का उपयोग करने वाली संस्थाओं के पास भी एक स्थिति.कॉम है।

आपके द्वारा हमें दी गई जानकारी के आधार पर ऐसा नहीं लगता है कि आपका RenderComponent एक TextureComponent और ShaderComponent में विभाजित होने के लिए एक अच्छा उम्मीदवार है, जैसा कि shader के Texture पर पूरी तरह निर्भर हैं और कुछ नहीं।

मान लें कि आप टी-मशीन के समान कुछ का उपयोग कर रहे हैं : एंटिटी सिस्टम C ++ में एक RenderComponent & RenderSystem का एक नमूना कार्यान्वयन कुछ इस तरह दिखाई देगा:

struct RenderComponent {
    Texture* textureData;
    Shader* shaderData;
};

class RenderSystem {
    public:
        RenderSystem(EntityManager& manager) :
            m_manager(manager) {
            // Initialize Window, rendering context, etc...
        }

        void update() {
            // Get all the entities with RenderComponent
            std::vector<RenderComponent>& components = m_manager.getComponents<RenderComponent>();

            for(auto component = components.begin(); entity != components.end(); ++components) {
                // Do something with the texture
                doSomethingWithTexture(component->textureData);

                // Do something with the shader if it's not null
                if(component->shaderData != nullptr) {
                    doSomethingWithShader(component->shaderData);
                }
            }
        }
    private:
        EntityManager& m_manager;
}

यह पूरी तरह से गलत है। घटकों का पूरा बिंदु उन्हें संस्थाओं से अलग करना है, उन्हें खोजने के लिए संस्थाओं के माध्यम से रेंडर सिस्टम खोज नहीं करना है। रेंडर सिस्टम को अपने डेटा को पूरी तरह से नियंत्रित करना चाहिए। PS स्टॉप्स :: वेक्टर (विशेष रूप से इंस्टेंस डेटा के साथ) लूप्स में नहीं डालते, यह भयानक (धीमा) C ++ है।
स्नेक 5

@ स्नेक 5 आप दोनों गणनाओं पर सही हैं। मैंने अपने सिर के ऊपर से कोड टाइप किया और कुछ समस्याएँ थीं, उन्हें इंगित करने के लिए धन्यवाद। मैंने प्रभावित कोड को कम धीमी और सही ढंग से इकाई प्रणाली मुहावरों का उपयोग करने के लिए तय किया है।
जेक वुड्स

2
@ snake5 आप डेटा को हर फ्रेम में पुनर्गणना नहीं कर रहे हैं, getCompords m_manager के स्वामित्व वाला एक वेक्टर देता है जो पहले से ही जाना जाता है और घटकों को जोड़ने / हटाने पर केवल बदलता है। यह एक फायदा है जब आपके पास एक ऐसी प्रणाली होती है जो एक ही इकाई के कई घटकों का उपयोग करना चाहती है, उदाहरण के लिए एक PhysicsSystem जो स्थितिComponent और PhysicsComponent का उपयोग करना चाहती है। अन्य सिस्टम शायद स्थिति चाहते हैं और एक स्थितिComponent होने से आपके पास डुप्लिकेट डेटा नहीं है। मुख्य रूप से यह समस्या को हल करता है कि घटक कैसे संवाद करते हैं।
जेक वुड्स

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

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

2

ख़तरा # 1: अतिदेय कोड। इस बारे में सोचें कि क्या आपको वास्तव में आपके द्वारा लागू की जाने वाली प्रत्येक चीज की आवश्यकता है क्योंकि आप इसके साथ कुछ समय के लिए रहने वाले हैं।

नुकसान # 2: बहुत अधिक वस्तुओं। मैं बहुत सी वस्तुओं (प्रत्येक प्रकार, उपप्रकार और जो भी हो) के साथ एक प्रणाली का उपयोग नहीं करेगा क्योंकि यह सिर्फ स्वचालित प्रसंस्करण को कठिन बनाता है। मेरी राय में, प्रत्येक वस्तु को एक निश्चित फीचर सेट (एक फीचर के विपरीत) को नियंत्रित करना बहुत अच्छा लगता है। उदाहरण के लिए, रेंडरिंग (टेक्सचर कंपोनेंट, शेडर कंपोनेंट) में शामिल प्रत्येक बिट डेटा के लिए कंपोनेंट्स को बहुत अधिक विभाजित किया गया है - आपको आमतौर पर उन सभी कंपोनेंट्स को एक साथ रखने की आवश्यकता होगी, क्या आप सहमत नहीं होंगे?

नुकसान # 3: बहुत सख्त बाहरी नियंत्रण। शेयर्ड / टेक्सचर ऑब्जेक्ट्स में नाम बदलने को प्राथमिकता दें क्योंकि ऑब्जेक्ट्स रेंडरर / टेक्सचर टाइप / shader फॉर्मेट / जो भी हो, के साथ बदल सकते हैं। नाम सरल पहचानकर्ता हैं - यह रेंडर करने वाले पर निर्भर करता है कि वे क्या तय करें। एक दिन आप सादे शेड्स के बजाय सामग्री जोड़ना चाह सकते हैं (उदाहरण के लिए, शेड्स, टेक्सचर्स और डेटा से ब्लेंड मोड जोड़ें)। पाठ-आधारित इंटरफ़ेस के साथ, इसे लागू करना बहुत आसान है।

रेंडरर के रूप में, यह एक सरल इंटरफ़ेस हो सकता है जो घटकों द्वारा बनाई गई वस्तुओं को बनाता / नष्ट / अनुरक्षित / बनाए रखता है / प्रदान करता है। इसका सबसे आदिम प्रतिनिधित्व कुछ इस तरह से हो सकता है:

class Renderer {
    function Draw() { ... }
    function AddSprite( ... ) { ... return sprite; }
    function RemoveSprite( sprite ) { ... }
    ...
};

यह आपको अपने घटकों से इन वस्तुओं का प्रबंधन करने और उन्हें दूर रखने के लिए पर्याप्त अनुमति देगा ताकि आप उन्हें किसी भी तरह से प्रस्तुत कर सकें।

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